#Authors: Ian McElveen and Cecilia Gonzales
#Author Date: 7/14/2025
#Purpose: The purpose of this notebook is to house all data set transformation, cleansing, visualization, statistical analysis, and note-taking for the 2025 CU Athletic Department Sports Science Internship Program

#LAST UPDATED: 8/5/2025

#Including helpful libraries
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ───────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(readxl)
library(aod)
library(gt)
G2;H2;Warningh: package ‘gt’ was built under R version 4.5.1g
library(boot)
library(mgcv)
G3;Loading required package: nlme
gG3;
Attaching package: ‘nlme’

gG3;The following object is masked from ‘package:dplyr’:

    collapse

gG3;This is mgcv 1.9-1. For overview type 'help("mgcv-package")'.
gG3;
Attaching package: ‘mgcv’

gG3;The following object is masked from ‘package:aod’:

    negbin

g
library(lme4)
G3;Loading required package: Matrix
gG3;
Attaching package: ‘Matrix’

gG3;The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack

gG3;
Attaching package: ‘lme4’

gG3;The following object is masked from ‘package:nlme’:

    lmList

g
library(leaps)
G2;H2;Warningh: package ‘leaps’ was built under R version 4.5.1g

Data Cleaning

#loading in the Catapult data to look at sprinting values
Catapult_Session <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Catapult Session - Outdoor FB.csv")
New names:
• `` -> `...1`
Rows: 77140 Columns: 491
── Column specification ───────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (33): anon_id, Date, Gender, Sport, Primary.Position, Day.of.the.Week, Month, Month.w...
dbl  (427): ...1, Age, Year, Period.Number, Total.Duration..min., Total.Distance, Velocity....
lgl   (27): Deceleration.B3.Total.Effort, Deceleration.B2.Total.Effort, Deceleration.B1.Tot...
time   (4): Start, End, Total.Duration, Field.Time

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#loading in the Historical Running data to look at running imbalance values
Historical_Running <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Compiled Historical Running Imbalance FB.csv")
Rows: 9942 Columns: 4
── Column specification ───────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): anon_id, Name, Date, Running.Imbalance

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#loading in the Incident Report to look at HSIs
Incident_Report <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Incident Report FB IDs.csv")
New names:
• `` -> `...1`
G2;H2;Warningh: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)g
Rows: 2220 Columns: 225
── Column specification ───────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (163): anon_id, Date, Sport, Position, Medical.Alerts, View.Medical.Alerts, Incident.T...
dbl   (31): ...1, Days.to.Exam, Days.in.Status, Games.Missed, Injury.to.Exam.Duration, Inju...
lgl   (28): MRN, Converted.to.injury.from.maintenance.record, What.sport.is.this.incident.r...
time   (3): Time.of.Injury, Time.Entered.On, Time.Stamp

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Catapult_Session_clean <- Catapult_Session %>%
  #putting the date as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only selecting important columns for this analysis
  select(anon_id, Date, Age, Primary.Position, Total.Distance, Period.Name, Total.Duration..min., Velocity.Band.1.Total.Distance, Velocity.Band.2.Total.Distance, Velocity.Band.3.Total.Distance, Velocity.Band.4.Total.Distance, Velocity.Band.5.Total.Distance, Velocity.Band.6.Total.Distance, Velocity.Band.7.Total.Distance, Velocity.Band.8.Total.Distance, Velocity.Band.2.Total.Effort.Count, Velocity.Band.3.Total.Effort.Count, Velocity.Band.4.Total.Effort.Count, Velocity.Band.5.Total.Effort.Count, Velocity.Band.6.Total.Effort.Count, Velocity.Band.7.Total.Effort.Count, Velocity.Band.8.Total.Effort.Count, Maximum.Velocity, Average.Velocity, Hit.90.Percent.Max, Date.of.Last.90.Effort, Days.Since.Last.90.Effort, Hit.Max.Velocity., Date.of.All.Time.Max.Velocity, Days.Since.Max.Velocity, Session.Max.Velocity) %>%
  #calculating each player's maximum velocity
  group_by(anon_id) %>%
  mutate(Player.Max.Velocity = max(na.omit(Maximum.Velocity))) %>%
  ungroup() %>%
  #only selecting data from January 1, 2024 and on
  filter(Date >= "2024-01-01")

head(Catapult_Session_clean)
Historical_Running_clean <- Historical_Running %>%
  #taking out rows that don't have data
  filter(Running.Imbalance != "n/a") %>%
  #putting running imbalance as a number and converting the date to a date class
  mutate(Running.Imbalance = as.numeric(Running.Imbalance),
         Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only using data from January 1, 2024 and on
  filter(Date >= "2024-01-01") %>%
  mutate(X=1:4063) %>%
  #making days since January 1, 2024 for each player
  group_by(anon_id) %>%
  mutate(Days.Since.Start = as.numeric(Date - min(Date))) %>%
  ungroup()

head(Historical_Running_clean)
Incident_Report_clean <- Incident_Report %>%
  #filtering for only hamstring injuries
  filter(OSICS14.Code == "TM1",
         Status != "Full Go")  %>%
  #getting the date of the injury as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y"),
         Date.of.Injury = as.Date(Date.of.Injury...Onset.of.symptoms, "%m/%d/%Y"),
         Examination.Date = as.Date(Examination.Date, "%m/%d/%Y")) %>%
  #only selecting relevant columns for this analysis
  select(anon_id, Position, Date, Date.of.Injury, Time.of.Injury, Side, OSICS.Injury.Diagnosis, Coach.s.Diagnosis, Recurrence.of.Injury, Choose.Season, Onset.of.Symptoms, Injury.Prognosis, General.Mechanism, Specific.Mechanism, Injured.While., Type.of.Event, Season., Status, Days.in.Status) %>%
  #making days out due to injury for each player and each injury they sustained
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Days.Out = sum(Days.in.Status)) %>%
  ungroup()

head(Incident_Report_clean)
#taking the IDs of players who are and aren't injured
all_IDs <- unique(Historical_Running_clean$anon_id)
#taking IDs that were injured and also have running imbalance data
injured_IDs <- intersect(unique(Incident_Report_clean$anon_id), all_IDs)
#taking all players with running imbalance data that don't have an injury
uninjured_IDs <- unique((Historical_Running_clean %>%
  filter(!anon_id %in% injured_IDs))$anon_id)
#injured players who also have running imbalance data
Incident_Report_clean <- Incident_Report_clean %>%
  filter(anon_id %in% injured_IDs)

#all players that only have running imbalance data or have both running imbalance data and incidence report
Historical_Running_clean <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs | anon_id %in% uninjured_IDs)
#removing uncleaned data sets
remove(Incident_Report)
remove(Historical_Running)
remove(Catapult_Session)

Section 1: Running Speed

How often are athletes reaching ≥ 90% maximum velocity throughout a training season?

# Bar chart for how often players reach ≥ 90% maximum velocity

# Count for how many times each anon_id hit ≥ 90% maximum velocity
hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% # Filter for training season
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, Primary.Position) %>%
  group_by(anon_id, Primary.Position) %>%
  summarise(times_hit_90 = n(), .groups = "drop") %>%
  mutate(
    Position_Group = case_when(
      Primary.Position %in% c("QB", "LB", "TE", "RB") ~ "COMBO",
      Primary.Position %in% c("OL", "DL", "DE") ~ "BIGS",
      Primary.Position %in% c("WR", "DB", "DB, WR") ~ "SKILL",
      TRUE ~ "OTHER"
    ))

# Plot of all players' frequencies
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_hline(yintercept = mean(hit_90_counts$times_hit_90), 
             linetype = "dashed", color = "#565A5C", linewidth = 0.5) +
  labs(title = "Player Counts for Achieving ≥ 90% of Maximum Velocity During 2024–25 Season", 
       subtitle = paste("Team Average:", round(mean(hit_90_counts$times_hit_90), 1)),
       x = "Athlete ID", y = "Times ≥ 90%") +
  theme_classic() +
  theme(axis.text.x = element_text(size = 6, angle = 90))

Do not need because we created a function to plot each position

# Bar chart of times reached >90%, Quarterbacks

# Filter to only have data for QBs
QBs <- hit_90_counts %>%
  filter(Primary.Position == "QB")

# Calculate the averages first
overall_avg <- mean(hit_90_counts$times_hit_90)
qb_avg <- mean(QBs$times_hit_90)

ggplot(QBs, aes(x=anon_id, y=times_hit_90)) +
  geom_bar(stat="identity", fill = "#CFB87C") + 
  geom_text(aes(label = times_hit_90), 
            vjust = -0.5, 
            size = 3.5) +
  geom_hline(yintercept = overall_avg, 
             linetype = "dashed", color = "#000000") +
  geom_hline(yintercept = qb_avg, 
             linetype = "dashed", color = "#565A5C") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3) +
  annotate("text", x = 1, y = qb_avg + 0.5, 
           label = "QB Avg", color = "#565A5C", size = 3) +
  labs(title = "QB Counts for Reaching ≥90% Max Velocity", 
       x = "Athlete ID", y = "Times ≥90%",
       subtitle = paste("QB Average:", qb_avg)) +
  theme_classic()

Facet plot useful, but hard to read so probably don’t include in presentation.

# Facet Plot

# Calculate overall average
overall_avg <- mean(hit_90_counts$times_hit_90)

# Plot faceted bar charts by position
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
  geom_hline(yintercept = overall_avg, linetype = "dashed", color = "#000000") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
  facet_wrap(~ Primary.Position, scales = "free_x") +
  labs(title = "Counts of ≥90% Max Velocity by Player and Position",
       y = "Times ≥90% Max Velocity",
       x = "Athlete ID") +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Plots for each position

# Get the overall team average once from hit_90_counts
team_avg <- mean(hit_90_counts$times_hit_90)

# Define  positions to loop through
positions <- unique(hit_90_counts$Primary.Position)

# Plotting function:
plot_hit_90_by_position <- function(pos) {
  position_data <- hit_90_counts %>%
    filter(Primary.Position == pos)
  
  pos_avg <- mean(position_data$times_hit_90)
  
  ggplot(position_data, aes(x = anon_id, y = times_hit_90)) +
    geom_bar(stat = "identity", fill = "#CFB87C") +
    geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
    geom_hline(yintercept = pos_avg, linetype = "dashed", color = "#565A5C") +
    annotate("text", x = 1, y = pos_avg + 0.5, 
             label = "Pos Avg", color = "#565A5C", size = 3, hjust = 0) +
    geom_hline(yintercept = team_avg, linetype = "dashed", color = "#000000") +
    annotate("text", x = 1, y = team_avg + 0.5, 
             label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
    labs(title = paste("Times ≥90% Max Velocity –", pos),
         subtitle = paste0("Position Avg: ", round(pos_avg, 1),
                           " | Team Avg: ", round(team_avg, 1)),
         y = "Count",
         x = "anon_id") +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Loop through each position and print the plot
plots_by_position <- lapply(positions, function(pos) {
  print(plot_hit_90_by_position(pos))
})

# Table for average values of each position

position_averages <- hit_90_counts %>%
  group_by(Primary.Position) %>%
  summarise(avg_times_hit_90 = mean(times_hit_90), .groups = "drop") %>%
  arrange(desc(avg_times_hit_90))

position_averages_with_team <- bind_rows(
  tibble(
    Primary.Position = "Team Average",
    avg_times_hit_90 = team_avg
  ),
  position_averages
)

position_averages_with_team %>%
  gt() %>%
  tab_header(
    title = "Average Times ≥90% Max Velocity by Position and Team"
  )
Average Times ≥90% Max Velocity by Position and Team
Primary.Position avg_times_hit_90
Team Average 11.768421
DB 18.352941
WR 15.666667
DL 10.789474
RB 10.500000
QB 9.750000
LB 8.916667
TE 8.400000
OL 6.764706

DBs have the highest average times reaching ≥90% of Max velocity (18.4). The OL had the lowest (6.8). Team average was 11.8.

# Plot counts for each position group

# Get the overall team average once from hit_90_counts
team_avg <- mean(hit_90_counts$times_hit_90)

# Define  positions to loop through
positions_groups <- unique(hit_90_counts$Position_Group)

# Plotting function:
plot_hit_90_group <- function(pos) {
  group_data <- hit_90_counts %>%
    filter(Position_Group == pos)
  
  group_avg <- mean(group_data$times_hit_90)
  
  ggplot(group_data, aes(x = anon_id, y = times_hit_90)) +
    geom_bar(stat = "identity", fill = "#CFB87C") +
    geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
    geom_hline(yintercept = group_avg, linetype = "dashed", color = "#565A5C") +
    annotate("text", x = 1, y = group_avg + 0.5, 
             label = "Group Avg", color = "#565A5C", size = 3, hjust = 0) +
    geom_hline(yintercept = team_avg, linetype = "dashed", color = "#000000") +
    annotate("text", x = 1, y = team_avg + 0.5, 
             label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
    labs(title = paste("Times ≥90% Max Velocity –", pos),
         subtitle = paste0("Group Avg: ", round(group_avg, 1),
                           " | Team Avg: ", round(team_avg, 1)),
         y = "Count",
         x = "anon_id") +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Loop through each position and print the plot
plots_by_group <- lapply(positions_groups, function(pos) {
  print(plot_hit_90_group(pos))
})

The skill position group has the highest average times reached ≥90% Max with 17.1, then combo with 9.3, then bigs with 8.9.

# Make facet plot so that it fits on the presentation slides better.

# Calculate averages for each group and the team
group_avgs <- hit_90_counts %>%
  group_by(Position_Group) %>%
  summarise(group_avg = mean(times_hit_90))

team_avg <- mean(hit_90_counts$times_hit_90)

# Join group averages back to the main data
plot_data <- hit_90_counts %>%
  left_join(group_avgs, by = "Position_Group")

# Create label data with formatted strings
avg_labels <- plot_data %>%
  group_by(Position_Group) %>%
  summarise(
    group_avg = unique(group_avg),
    team_avg = team_avg,
    label_x = 1,
    group_label_y = group_avg + 1,
    team_label_y = team_avg + 1,
    group_label = round(group_avg, 1),
    team_label = round(team_avg, 1),
    .groups = "drop"
  )

# Plot
ggplot(plot_data, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_hline(aes(yintercept = group_avg), linetype = "dashed", color = "#000000") +
  geom_hline(yintercept = team_avg, linetype = "dotted", color = "#565A5C") +
  geom_text(data = avg_labels, aes(x = label_x, y = group_label_y, label = group_label),
            inherit.aes = FALSE, hjust = 0, size = 3, color = "#000000") +
  geom_text(data = avg_labels, aes(x = label_x, y = team_label_y, label = team_label),
            inherit.aes = FALSE, hjust = 0, size = 3, color = "#565A5C") +
  facet_wrap(~ Position_Group, scales = "free_x") +
  labs(title = "Times Reached ≥90% Max Velocity by Position Group",
       y = "Count", x = "anon_id") +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

# Create position group avg count table

# Create team average row
team_row <- tibble(
  Group = "Team Avg",
  Average = team_avg
)

# Create group average rows
group_rows <- hit_90_counts %>%
  group_by(Position_Group) %>%
  summarise(Average = mean(times_hit_90), .groups = "drop") %>%
  arrange(desc(Average)) %>%  # Sort from highest to lowest
  rename(Group = Position_Group)

# Combine them (team first)
combined_table <- bind_rows(team_row, group_rows)

# View the table
combined_table

No surprise that the skill position group has the highest average counts.

# >90% counts over the course of the season

# Trying to find out when are players reaching above 90% the most

# Create dataset that has the count of >90% of each day
daily_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date) %>%
  group_by(Date) %>%
  summarise(daily_hits = n())

# Plot
ggplot(daily_90_counts, aes(x = Date, y = daily_hits)) +
  geom_line(color = "#CFB87C", linewidth = 1) +
  geom_smooth(method = "loess", se = FALSE, color = "#565A5C", linetype = "dashed") +
  labs(title = "Daily Count of Players Reaching ≥ 90% of Max Velocity",
       subtitle = "Over the 2024–25 Training Season",
       x = "Date", y = "Times reached ≥ 90%") +
  theme_classic()

Data is noisy, so lets try grouping by week instead of every day.

# >90% counts for each week instead of each day. Will be a little less noisy than the daily
weekly_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date) %>%
  mutate(week = floor_date(Date, unit = "week")) %>%
  group_by(week) %>%
  summarise(weekly_hits = n())

mean_val <- round(mean(weekly_90_counts$weekly_hits), 1)

ggplot(weekly_90_counts, aes(x = week, y = weekly_hits)) +
  geom_line(color = "#CFB87C", linewidth = 1) +
  geom_point(color = "#000000") +
  geom_smooth(method = "loess", se = FALSE, color = "#565A5C", linetype = "dashed", linewidth = 0.6) +
  labs(title = "Weekly Count of Players Reaching ≥ 90% of Max Velocity",
       subtitle = paste0("Week Average: ", mean_val),
       x = "Week", y = "Times reached ≥ 90%") +
  theme_classic()

# Bar chart of weekly >90% counts

ggplot(weekly_90_counts, aes(x = week, y = weekly_hits)) +
  geom_col(fill = "#CFB87C", color = "#565A5C", width = 5) +
  geom_text(aes(label = weekly_hits), 
            vjust = -0.4, 
            size = 2.5, 
            color = "black") +
  geom_hline(yintercept = mean_val,
             linetype = "dashed", color = "#565A5C", linewidth = 0.5) +
  annotate("text", 
           x = min(weekly_90_counts$week) + 7,  # Adjust this date to place the label
           y = mean_val + 1.5,                 # Slightly above the line
           label = paste("Mean:", round(mean_val, 1)),
           size = 3.25, color = "#000000") +
  labs(
    title    = "Weekly Count of Players Reaching ≥ 90% of Max Velocity",
    subtitle = paste("Season Average per Week:", round(mean(weekly_90_counts$weekly_hits), 1)),
    x        = "Week",
    y        = "Times reached ≥ 90%"
  ) +
  scale_x_date(date_breaks = "2 weeks", date_labels = "%b %d") +
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Some of our lowest average counts for a week seem to be during the season, whereas some of our highest counts seem to be pre and post-season.

Should we consider the number of sprinting efforts that athletes are completing?

How often athletes are hitting which velocity bands in relation to their top speeds.

# Create sum of weekly band totals

# Make sure week is defined
Catapult_Session_clean <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% 
  mutate(week = floor_date(Date, unit = "week"))

# Sum efforts by week and athlete
weekly_velocity_efforts <- Catapult_Session_clean %>%
  group_by(anon_id, week) %>%
  summarise(
    V2 = sum(Velocity.Band.2.Total.Effort.Count, na.rm = TRUE),
    V3 = sum(Velocity.Band.3.Total.Effort.Count, na.rm = TRUE),
    V4 = sum(Velocity.Band.4.Total.Effort.Count, na.rm = TRUE),
    V5 = sum(Velocity.Band.5.Total.Effort.Count, na.rm = TRUE),
    V6 = sum(Velocity.Band.6.Total.Effort.Count, na.rm = TRUE),
    V7 = sum(Velocity.Band.7.Total.Effort.Count, na.rm = TRUE),
    V8 = sum(Velocity.Band.8.Total.Effort.Count, na.rm = TRUE),
    total_efforts = V2 + V3 + V4 + V5 + V6 + V7 + V8,
    Weekly_Max_Velocity = max(Maximum.Velocity, na.rm = TRUE),
    Player_Max_Velocity = first(Player.Max.Velocity),
    .groups = "drop"
  ) %>%
  mutate(
    pct_of_max_velocity = (Weekly_Max_Velocity / Player_Max_Velocity) * 100
  )

# Create percentage of weekly efforts in each band
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  mutate(
    pct_V2 = (V2 / total_efforts) * 100,
    pct_V3 = (V3 / total_efforts) * 100,
    pct_V4 = (V4 / total_efforts) * 100,
    pct_V5 = (V5 / total_efforts) * 100,
    pct_V6 = (V6 / total_efforts) * 100,
    pct_V7 = (V7 / total_efforts) * 100,
    pct_V8 = (V8 / total_efforts) * 100
  )

weekly_velocity_efforts <- weekly_velocity_efforts %>%
  filter(total_efforts > 0)
# Plot for each player

# Loop over all players and display their plots
unique(weekly_velocity_efforts$anon_id) %>%
  lapply(function(player_id) {
    
    # Prepare the data for one player
    player_weekly_data <- weekly_velocity_efforts %>%
      filter(anon_id == player_id) %>%
      select(week, pct_V2:pct_V8, pct_of_max_velocity) %>%
      pivot_longer(
        cols = starts_with("pct_V"),
        names_to = "velocity_band",
        values_to = "percent_effort"
      ) %>%
      mutate(
        velocity_band = factor(
          velocity_band,
          levels = paste0("pct_V", 8:2),
          labels = paste0("V", 8:2)
        )
      )
    
    # Skip empty data
    if (nrow(player_weekly_data) == 0) return(NULL)
    
    # Generate and print the plot
    plot <- ggplot(player_weekly_data, aes(x = week)) +
      geom_col(aes(y = percent_effort, fill = velocity_band), position = "stack") +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2" = "darkgreen",
          "V3" = "green2",
          "V4" = "greenyellow",
          "V5" = "yellow",
          "V6" = "orange",
          "V7" = "tomato",
          "V8" = "firebrick"
  )
) +
    scale_y_continuous(
        name = "Band Distribution (%)",
        sec.axis = sec_axis(~ ., name = "Weekly Max Velocity (% of PR)")
      ) +
      labs(
        title = paste("Velocity Band Effort % and Speed Trend for Player", player_id),
        x = "Week",
        fill = "Velocity Band"
      ) +
      theme_classic()
    
    print(plot)  # Display plot
    
    return(NULL)
  })
G2;H2;Warningh: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_]8;;ide:run:warnings()warnings()]8;;` to see where this warning was generated.g
[[1]]
NULL

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[5]]
NULL

[[6]]
NULL

[[7]]
NULL

[[8]]
NULL

[[9]]
NULL

[[10]]
NULL

[[11]]
NULL

[[12]]
NULL

[[13]]
NULL

[[14]]
NULL

[[15]]
NULL

[[16]]
NULL

[[17]]
NULL

[[18]]
NULL

[[19]]
NULL

[[20]]
NULL

[[21]]
NULL

[[22]]
NULL

[[23]]
NULL

[[24]]
NULL

[[25]]
NULL

[[26]]
NULL

[[27]]
NULL

[[28]]
NULL

[[29]]
NULL

[[30]]
NULL

[[31]]
NULL

[[32]]
NULL

[[33]]
NULL

[[34]]
NULL

[[35]]
NULL

[[36]]
NULL

[[37]]
NULL

[[38]]
NULL

[[39]]
NULL

[[40]]
NULL

[[41]]
NULL

[[42]]
NULL

[[43]]
NULL

[[44]]
NULL

[[45]]
NULL

[[46]]
NULL

[[47]]
NULL

[[48]]
NULL

[[49]]
NULL

[[50]]
NULL

[[51]]
NULL

[[52]]
NULL

[[53]]
NULL

[[54]]
NULL

[[55]]
NULL

[[56]]
NULL

[[57]]
NULL

[[58]]
NULL

[[59]]
NULL

[[60]]
NULL

[[61]]
NULL

[[62]]
NULL

[[63]]
NULL

[[64]]
NULL

[[65]]
NULL

[[66]]
NULL

[[67]]
NULL

[[68]]
NULL

[[69]]
NULL

[[70]]
NULL

[[71]]
NULL

[[72]]
NULL

[[73]]
NULL

[[74]]
NULL

[[75]]
NULL

[[76]]
NULL

[[77]]
NULL

[[78]]
NULL

[[79]]
NULL

[[80]]
NULL

[[81]]
NULL

[[82]]
NULL

[[83]]
NULL

[[84]]
NULL

[[85]]
NULL

[[86]]
NULL

[[87]]
NULL

[[88]]
NULL

[[89]]
NULL

[[90]]
NULL

[[91]]
NULL

[[92]]
NULL

[[93]]
NULL

[[94]]
NULL

[[95]]
NULL

[[96]]
NULL

[[97]]
NULL

[[98]]
NULL

[[99]]
NULL

[[100]]
NULL

[[101]]
NULL

[[102]]
NULL

[[103]]
NULL

[[104]]
NULL

Each player is different in reaching different velocity bands in relation to their top speeds. Some players, ID_11 for example, rarely enter bands 6 or higher, even if they are close to 100% of their max velocity. This shows the issues with using absolute bands for the whole team.

# Filter for ID_11 and reshape the data
player_weekly_data <- weekly_velocity_efforts %>%
  filter(anon_id == "ID_11") %>%
  select(week, pct_V2:pct_V8, pct_of_max_velocity) %>%
  pivot_longer(
    cols = starts_with("pct_V"),
    names_to = "velocity_band",
    values_to = "percent_effort"
  ) %>%
  mutate(
    velocity_band = factor(
      velocity_band,
      levels = paste0("pct_V", 8:2),
      labels = paste0("V", 8:2)
    )
  )

# Plot for ID_11
ggplot(player_weekly_data, aes(x = week)) +
  geom_col(aes(y = percent_effort, fill = velocity_band), position = "stack") +
  geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
  geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
  scale_fill_manual(
    values = c(
      "V2" = "darkgreen",
      "V3" = "green2",
      "V4" = "greenyellow",
      "V5" = "yellow",
      "V6" = "orange",
      "V7" = "tomato",
      "V8" = "firebrick"
    )
  ) +
  scale_y_continuous(
    name = "Band Distribution (%)",
    sec.axis = sec_axis(~ ., name = "Weekly Max Velocity (% of PR)")
  ) +
  labs(
    title = "Velocity Band Effort % and Speed Trend for Player ID_11",
    x = "Week",
    fill = "Velocity Band"
  ) +
  theme_classic()

# Pick one player
player_id <- "ID_93"

# Filter data for that player and weeks
player_data <- weekly_velocity_efforts %>%
  filter(anon_id == player_id)

# Plot of Total Sprinting Efforts per Week
ggplot(player_data, aes(x = week, y = total_efforts)) +
  geom_line(color = "#CFB87C", size = 1) +
  geom_point(color = "#CFB87C", size = 2) +
  labs(
    title = paste("Total Sprinting Efforts per Week for Player", player_id),
    x = "Week",
    y = "Total Sprinting Efforts"
  ) +
  theme_classic()


#Plot of Sprint Effort Distribution by Velocity Band

# Reshape absolute counts to long format
player_counts_long <- player_data %>%
  select(week, V2, V3, V4, V5, V6, V7, V8) %>%
  pivot_longer(
    cols = V2:V8,
    names_to = "velocity_band",
    values_to = "effort_count"
  ) %>%
  mutate(
    velocity_band = factor(velocity_band, levels = paste0("V", 8:2))
  )

ggplot(player_counts_long, aes(x = week, y = effort_count, fill = velocity_band)) +
  geom_col(position = "stack") +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = paste("Sprint Effort Distribution by Velocity Band for Player", player_id),
    x = "Week",
    y = "Sprint Effort Count",
    fill = "Velocity Band"
  ) +
  theme_classic()

Exploring correlations between bands and max speeds



# Select relevant columns
cor_data <- weekly_velocity_efforts %>%
  select(pct_of_max_velocity, pct_V2, pct_V3, pct_V4, pct_V5, pct_V6, pct_V7, pct_V8)

# Compute correlation matrix
cor_matrix <- cor(cor_data, use = "pairwise.complete.obs")

cor_matrix
                    pct_of_max_velocity     pct_V2      pct_V3     pct_V4     pct_V5
pct_of_max_velocity          1.00000000 -0.5213861  0.05869457  0.4422664  0.4482732
pct_V2                      -0.52138614  1.0000000 -0.15363378 -0.8795243 -0.8605602
pct_V3                       0.05869457 -0.1536338  1.00000000  0.3272849 -0.2133725
pct_V4                       0.44226643 -0.8795243  0.32728485  1.0000000  0.6831749
pct_V5                       0.44827321 -0.8605602 -0.21337249  0.6831749  1.0000000
pct_V6                       0.41150125 -0.7765560 -0.37704110  0.4754871  0.8333338
pct_V7                       0.33727593 -0.6129867 -0.43386873  0.2712374  0.5853417
pct_V8                       0.33778410 -0.5101834 -0.42332967  0.1965351  0.4713043
                        pct_V6     pct_V7     pct_V8
pct_of_max_velocity  0.4115013  0.3372759  0.3377841
pct_V2              -0.7765560 -0.6129867 -0.5101834
pct_V3              -0.3770411 -0.4338687 -0.4233297
pct_V4               0.4754871  0.2712374  0.1965351
pct_V5               0.8333338  0.5853417  0.4713043
pct_V6               1.0000000  0.8067562  0.6559091
pct_V7               0.8067562  1.0000000  0.8731903
pct_V8               0.6559091  0.8731903  1.0000000
# Compute correlations with pct_of_max_velocity
cor_values <- cor_data %>%
  summarise(across(-pct_of_max_velocity,
                   ~ cor(.x, cor_data$pct_of_max_velocity, use = "pairwise.complete.obs"))) %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "correlation")

# Plot with labels
ggplot(cor_values, aes(x = reorder(variable, correlation), y = correlation)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(correlation, 2)), 
            hjust = ifelse(cor_values$correlation >= 0, -0.1, 1.1), 
            color = "black", size = 3) +
  coord_flip() +
  geom_hline(yintercept = 0, color = "black", linetype = "dashed") +
  labs(
    title = "Correlation Between Percent in Band and Percent of Max Velocity",
    x = "Band",
    y = "Pearson Correlation"
  ) +
  theme_classic() 

V4 and V5 appear to be slightly more predictive of top-speed achievement than V6, V7 or V8 — possibly because they’re more frequently reached zones.

V2 has a moderately strong negative linear relationship with reaching top-speed. When athletes spend more effort in this low-speed band, their weekly top speed (as % of max) tends to be lower.

# Linear model with all bands predicting pct_of_max_velocity
model_all_bands <- lm(
  pct_of_max_velocity ~ pct_V2 + pct_V3 + pct_V4 + pct_V5 + pct_V6 + pct_V7 + pct_V8,
  data = weekly_velocity_efforts
)
summary(model_all_bands)

Call:
lm(formula = pct_of_max_velocity ~ pct_V2 + pct_V3 + pct_V4 + 
    pct_V5 + pct_V6 + pct_V7 + pct_V8, data = weekly_velocity_efforts)

Residuals:
    Min      1Q  Median      3Q     Max 
-60.296  -4.649   1.038   5.861  24.418 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 296.9092    32.0021   9.278  < 2e-16 ***
pct_V2       -2.3814     0.3220  -7.396 2.11e-13 ***
pct_V3       -1.8554     0.3234  -5.737 1.12e-08 ***
pct_V4       -1.9669     0.3371  -5.834 6.35e-09 ***
pct_V5       -1.8813     0.3535  -5.322 1.15e-07 ***
pct_V6       -1.7832     0.3502  -5.092 3.90e-07 ***
pct_V7       -3.1034     0.5924  -5.238 1.80e-07 ***
pct_V8            NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.707 on 1872 degrees of freedom
Multiple R-squared:  0.2859,    Adjusted R-squared:  0.2836 
F-statistic: 124.9 on 6 and 1872 DF,  p-value: < 2.2e-16
# Compute change in top speeds week-to-week
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  arrange(anon_id, week) %>%
  group_by(anon_id) %>%
  mutate(
    pct_of_max_velocity_change = pct_of_max_velocity - lag(pct_of_max_velocity)
  ) %>%
  ungroup()

weekly_velocity_efforts <- weekly_velocity_efforts %>%
  group_by(anon_id) %>%
  mutate(
    lag_V2 = lag(V2),
    lag_V3 = lag(V3),
    lag_V4 = lag(V4),
    lag_V5 = lag(V5),
    lag_V6 = lag(V6),
    lag_V7 = lag(V7),
    lag_V8 = lag(V8),
    
    lag_pct_V2 = lag(pct_V2),
    lag_pct_V3 = lag(pct_V3),
    lag_pct_V4 = lag(pct_V4),
    lag_pct_V5 = lag(pct_V5),
    lag_pct_V6 = lag(pct_V6),
    lag_pct_V7 = lag(pct_V7),
    lag_pct_V8 = lag(pct_V8)
  ) %>%
  ungroup()
# Maybe group by position

# Get Player positions
player_positions <- Catapult_Session_clean %>%
  select(anon_id, Primary.Position) %>%
  distinct()

# Join Position into weekly_velocity_efforts
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  left_join(player_positions, by = "anon_id")
G2;H2;Warningh in left_join(., player_positions, by = "anon_id") :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 310 of `x` matches multiple rows in `y`.
ℹ Row 78 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence
  this warning.g
# Position groups
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  mutate(
    Position_Group = case_when(
      Primary.Position %in% c("QB", "LB", "TE", "RB") ~ "COMBO",
      Primary.Position %in% c("OL", "DL", "DE") ~ "BIGS",
      Primary.Position %in% c("WR", "DB", "DB, WR") ~ "SKILL",
      TRUE ~ "OTHER"
    )
  ) %>%
  filter(Position_Group != "OTHER")
G1;Error in `mutate()`:
ℹ In argument: `Position_Group = case_when(...)`.
Caused by error in `case_when()`:
! Failed to evaluate the left-hand side of formula 1.
Caused by error:
! object 'Primary.Position' not found
Run `]8;;x-r-run:rlang::last_trace()rlang::last_trace()]8;;` to see where the error occurred.
g
weekly_band_effort_by_group <- weekly_velocity_efforts %>%
  group_by(week, Position_Group) %>%
  summarise(
    mean_V2 = mean(V2, na.rm = TRUE),
    mean_V3 = mean(V3, na.rm = TRUE),
    mean_V4 = mean(V4, na.rm = TRUE),
    mean_V5 = mean(V5, na.rm = TRUE),
    mean_V6 = mean(V6, na.rm = TRUE),
    mean_V7 = mean(V7, na.rm = TRUE),
    mean_V8 = mean(V8, na.rm = TRUE),
    
    mean_lag_V2 = mean(lag_V2, na.rm = TRUE),
    mean_lag_V3 = mean(lag_V3, na.rm = TRUE),
    mean_lag_V4 = mean(lag_V4, na.rm = TRUE),
    mean_lag_V5 = mean(lag_V5, na.rm = TRUE),
    mean_lag_V6 = mean(lag_V6, na.rm = TRUE),
    mean_lag_V7 = mean(lag_V7, na.rm = TRUE),
    mean_lag_V8 = mean(lag_V8, na.rm = TRUE),
    
    mean_pct_V2 = mean(pct_V2, na.rm = TRUE),
    mean_pct_V3 = mean(pct_V3, na.rm = TRUE),
    mean_pct_V4 = mean(pct_V4, na.rm = TRUE),
    mean_pct_V5 = mean(pct_V5, na.rm = TRUE),
    mean_pct_V6 = mean(pct_V6, na.rm = TRUE),
    mean_pct_V7 = mean(pct_V7, na.rm = TRUE),
    mean_pct_V8 = mean(pct_V8, na.rm = TRUE),
    mean_weekly_max_velocity = mean(Weekly_Max_Velocity, na.rm = TRUE),
    mean_pct_max_velocity = mean(pct_of_max_velocity, na.rm = TRUE),
    mean_pct_max_velocity_change = mean(pct_of_max_velocity_change, na.rm = TRUE),
    n_players = n()
  ) %>%
  ungroup()
`summarise()` has grouped output by 'week'. You can override using the `.groups` argument.

# Pivot longer to get bands in one column for easier plotting
band_long <- weekly_band_effort_by_group %>%
  pivot_longer(
    cols = starts_with("mean_pct_V"),
    names_to = "velocity_band",
    values_to = "mean_pct_effort"
  ) %>%
  mutate(
    velocity_band = factor(velocity_band, levels = paste0("mean_pct_V", 2:8))
  )

ggplot(band_long, aes(x = week, y = mean_pct_effort, color = velocity_band)) +
  geom_line(size = 1) +
  facet_wrap(~ Position_Group) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group",
    x = "Week",
    y = "Mean % Effort",
    color = "Velocity Band"
  ) +
   scale_color_manual(
        values = c(
          "mean_pct_V2" = "darkgreen",
          "mean_pct_V3" = "green2",
          "mean_pct_V4" = "greenyellow",
          "mean_pct_V5" = "yellow",
          "mean_pct_V6" = "orange",
          "mean_pct_V7" = "tomato",
          "mean_pct_V8" = "firebrick"
  )
) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

This shows the mean percent of total efforts in each band over weeks by each position group.

# Plot mean weekly max velocity
ggplot(weekly_band_effort_by_group, aes(x = week, y = mean_weekly_max_velocity, color = Position_Group)) +
  geom_line(size = 1.2) +
  labs(
    title = "Mean Weekly Max Velocity by Position Group",
    x = "Week",
    y = "Mean Weekly Max Velocity"
  ) +
  theme_classic()



# Plot mean % max velocity change
ggplot(weekly_band_effort_by_group, aes(x = week, y = mean_pct_max_velocity_change, color = Position_Group)) +
  geom_line(size = 1.2) +
  labs(
    title = "Mean % Change in Max Velocity by Position Group",
    x = "Week",
    y = "Mean % Change in Max Velocity"
  ) +
  theme_classic()

ggplot() +
  # Plot the stacked lines for band effort %
  geom_line(data = band_long, aes(x = week, y = mean_pct_effort, color = velocity_band), size = 1) +
  geom_line(data = weekly_band_effort_by_group,
            aes(x = week, y = mean_weekly_max_velocity),
            color = "black", size = 1.2) +

  facet_wrap(~ Position_Group) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group\nwith Mean Weekly Max Velocity",
    x = "Week",
    y = "Mean % Effort",
    color = "Velocity Band"
  ) +
  scale_color_manual(
        values = c(
          "mean_pct_V2" = "darkgreen",
          "mean_pct_V3" = "green2",
          "mean_pct_V4" = "greenyellow",
          "mean_pct_V5" = "yellow",
          "mean_pct_V6" = "orange",
          "mean_pct_V7" = "tomato",
          "mean_pct_V8" = "firebrick"
  )
) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Define max effort and max velocity range
max_effort <- 67
max_velocity <- max(weekly_band_effort_by_group$mean_weekly_max_velocity, na.rm = TRUE)

ggplot() +
  geom_line(data = band_long, aes(x = week, y = mean_pct_effort, color = velocity_band), size = 1) +
  
  # Scale max_velocity to % effort scale to overlay on left axis
  geom_line(data = weekly_band_effort_by_group, 
            aes(x = week, y = (mean_weekly_max_velocity / max_velocity) * max_effort, color = "Max Velocity"),
            size = 1.1) +
  
  facet_wrap(~ Position_Group) +
  scale_y_continuous(
    name = "Mean % Effort",
    sec.axis = sec_axis(~ . * max_velocity / max_effort, name = "Mean Weekly Max Velocity (m/s)")
  ) +
  scale_color_manual(
    values = c(
      "mean_pct_V2" = "darkgreen",
      "mean_pct_V3" = "green2",
      "mean_pct_V4" = "greenyellow",
      "mean_pct_V5" = "yellow",
      "mean_pct_V6" = "orange",
      "mean_pct_V7" = "tomato",
      "mean_pct_V8" = "firebrick",
      "Max Velocity" = "black"
    )
  ) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group\nwith Mean Weekly Max Velocity",
    x = "Week",
    color = "Velocity Band"
  ) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Modeling

# Separate by position group
bigs <- weekly_band_effort_by_group %>%
  filter(Position_Group == "BIGS")

skill <- weekly_band_effort_by_group %>%
  filter(Position_Group == "SKILL")

combo <- weekly_band_effort_by_group %>%
  filter(Position_Group == "COMBO")

# Bigs Model
model_bigs <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = bigs
  )
summary(model_bigs)

Call:
lm(formula = mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + 
    mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, 
    data = bigs)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.82832 -0.35903 -0.01466  0.49791  2.67987 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 15.0559323  0.4742880  31.744   <2e-16 ***
mean_lag_V2  0.0007666  0.0162677   0.047    0.963    
mean_lag_V3 -0.0174581  0.0608015  -0.287    0.776    
mean_lag_V4  0.0124617  0.0702459   0.177    0.861    
mean_lag_V5  0.0980525  0.0990405   0.990    0.331    
mean_lag_V6 -0.2662617  0.2211796  -1.204    0.239    
mean_lag_V7  0.5251046  0.5567021   0.943    0.354    
mean_lag_V8  0.6964538  0.5708904   1.220    0.233    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.9501 on 27 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.4169,    Adjusted R-squared:  0.2658 
F-statistic: 2.758 on 7 and 27 DF,  p-value: 0.02682
# Skill Model
model_skill <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = skill
  )
summary(model_skill)

Call:
lm(formula = mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + 
    mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, 
    data = skill)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.3762 -0.4584  0.0883  0.6361  1.9607 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 16.875636   0.803735  20.997   <2e-16 ***
mean_lag_V2 -0.010484   0.020345  -0.515    0.610    
mean_lag_V3 -0.007903   0.053066  -0.149    0.883    
mean_lag_V4  0.015572   0.061910   0.252    0.803    
mean_lag_V5  0.036273   0.102055   0.355    0.725    
mean_lag_V6  0.066696   0.126898   0.526    0.603    
mean_lag_V7 -0.010326   0.149988  -0.069    0.946    
mean_lag_V8  0.077498   0.198001   0.391    0.698    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.295 on 29 degrees of freedom
Multiple R-squared:  0.2599,    Adjusted R-squared:  0.08124 
F-statistic: 1.455 on 7 and 29 DF,  p-value: 0.2224
# Combo Model
model_combo <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = combo)
summary(model_combo)

Call:
lm(formula = mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + 
    mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, 
    data = combo)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.8950 -0.3423  0.1298  0.4643  1.3618 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 17.85151    0.42494  42.009   <2e-16 ***
mean_lag_V2  0.01297    0.01314   0.987    0.332    
mean_lag_V3 -0.04224    0.03598  -1.174    0.251    
mean_lag_V4  0.01894    0.04929   0.384    0.704    
mean_lag_V5  0.06684    0.08694   0.769    0.449    
mean_lag_V6 -0.12756    0.16048  -0.795    0.434    
mean_lag_V7  0.17000    0.27613   0.616    0.543    
mean_lag_V8 -0.04655    0.26492  -0.176    0.862    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8189 on 27 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.2282,    Adjusted R-squared:  0.02812 
F-statistic: 1.141 on 7 and 27 DF,  p-value: 0.3681
# Group correlation:

# Select relevant variables
vars <- c("mean_weekly_max_velocity", "mean_lag_V2", "mean_lag_V3", 
          "mean_lag_V4", "mean_lag_V5", "mean_lag_V6", "mean_lag_V7", "mean_lag_V8")

# Correlation matrices
cor_bigs <- cor(bigs[, vars], use = "complete.obs")
cor_skill <- cor(skill[, vars], use = "complete.obs")
cor_combo <- cor(combo[, vars], use = "complete.obs")
round(cor_bigs, 2)
                         mean_weekly_max_velocity mean_lag_V2 mean_lag_V3 mean_lag_V4
mean_weekly_max_velocity                     1.00       -0.43       -0.38       -0.21
mean_lag_V2                                 -0.43        1.00        0.98        0.86
mean_lag_V3                                 -0.38        0.98        1.00        0.93
mean_lag_V4                                 -0.21        0.86        0.93        1.00
mean_lag_V5                                  0.16        0.28        0.36        0.60
mean_lag_V6                                  0.20        0.06        0.11        0.28
mean_lag_V7                                  0.35       -0.09       -0.03        0.12
mean_lag_V8                                  0.35        0.07        0.11        0.19
                         mean_lag_V5 mean_lag_V6 mean_lag_V7 mean_lag_V8
mean_weekly_max_velocity        0.16        0.20        0.35        0.35
mean_lag_V2                     0.28        0.06       -0.09        0.07
mean_lag_V3                     0.36        0.11       -0.03        0.11
mean_lag_V4                     0.60        0.28        0.12        0.19
mean_lag_V5                     1.00        0.83        0.61        0.27
mean_lag_V6                     0.83        1.00        0.88        0.39
mean_lag_V7                     0.61        0.88        1.00        0.62
mean_lag_V8                     0.27        0.39        0.62        1.00
round(cor_skill, 2)
                         mean_weekly_max_velocity mean_lag_V2 mean_lag_V3 mean_lag_V4
mean_weekly_max_velocity                     1.00        0.19        0.21        0.22
mean_lag_V2                                  0.19        1.00        1.00        0.99
mean_lag_V3                                  0.21        1.00        1.00        1.00
mean_lag_V4                                  0.22        0.99        1.00        1.00
mean_lag_V5                                  0.27        0.98        0.98        0.99
mean_lag_V6                                  0.35        0.92        0.93        0.94
mean_lag_V7                                  0.37        0.61        0.62        0.62
mean_lag_V8                                  0.40        0.55        0.56        0.56
                         mean_lag_V5 mean_lag_V6 mean_lag_V7 mean_lag_V8
mean_weekly_max_velocity        0.27        0.35        0.37        0.40
mean_lag_V2                     0.98        0.92        0.61        0.55
mean_lag_V3                     0.98        0.93        0.62        0.56
mean_lag_V4                     0.99        0.94        0.62        0.56
mean_lag_V5                     1.00        0.96        0.65        0.60
mean_lag_V6                     0.96        1.00        0.78        0.75
mean_lag_V7                     0.65        0.78        1.00        0.92
mean_lag_V8                     0.60        0.75        0.92        1.00
round(cor_combo, 2)
                         mean_weekly_max_velocity mean_lag_V2 mean_lag_V3 mean_lag_V4
mean_weekly_max_velocity                     1.00       -0.34       -0.33       -0.28
mean_lag_V2                                 -0.34        1.00        0.99        0.97
mean_lag_V3                                 -0.33        0.99        1.00        0.99
mean_lag_V4                                 -0.28        0.97        0.99        1.00
mean_lag_V5                                 -0.16        0.81        0.85        0.92
mean_lag_V6                                 -0.09        0.56        0.59        0.64
mean_lag_V7                                  0.04        0.17        0.19        0.23
mean_lag_V8                                  0.05        0.19        0.22        0.25
                         mean_lag_V5 mean_lag_V6 mean_lag_V7 mean_lag_V8
mean_weekly_max_velocity       -0.16       -0.09        0.04        0.05
mean_lag_V2                     0.81        0.56        0.17        0.19
mean_lag_V3                     0.85        0.59        0.19        0.22
mean_lag_V4                     0.92        0.64        0.23        0.25
mean_lag_V5                     1.00        0.83        0.46        0.43
mean_lag_V6                     0.83        1.00        0.84        0.71
mean_lag_V7                     0.46        0.84        1.00        0.89
mean_lag_V8                     0.43        0.71        0.89        1.00
# Reduce for better modeling
bigs <- bigs %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3) / 2,
    mid_band = (mean_lag_V4 + mean_lag_V5 + mean_lag_V6) / 3,
    high_band = (mean_lag_V7 + mean_lag_V8) / 2
  )
model_bigs_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = bigs)
summary(model_bigs_simple)

Call:
lm(formula = mean_weekly_max_velocity ~ low_band + mid_band + 
    high_band, data = bigs)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.62517 -0.48854 -0.01442  0.30496  2.80648 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 15.132025   0.468747  32.282   <2e-16 ***
low_band    -0.007602   0.003136  -2.424   0.0214 *  
mid_band     0.035535   0.034062   1.043   0.3049    
high_band    0.462722   0.320459   1.444   0.1588    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.9492 on 31 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.3317,    Adjusted R-squared:  0.267 
F-statistic: 5.129 on 3 and 31 DF,  p-value: 0.005368
# Skill Group
skill <- skill %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3 + mean_lag_V4) / 3,
    mid_band = (mean_lag_V5 + mean_lag_V6) / 2,
    high_band = (mean_lag_V7 + mean_lag_V8) / 2
  )
model_skill_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = skill)
summary(model_skill_simple)

Call:
lm(formula = mean_weekly_max_velocity ~ low_band + mid_band + 
    high_band, data = skill)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.3878 -0.3887  0.0538  0.6168  1.9999 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 16.833855   0.671421  25.072   <2e-16 ***
low_band    -0.019113   0.009036  -2.115   0.0420 *  
mid_band     0.124022   0.058472   2.121   0.0415 *  
high_band    0.051080   0.082001   0.623   0.5376    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.218 on 33 degrees of freedom
Multiple R-squared:  0.2539,    Adjusted R-squared:  0.1861 
F-statistic: 3.743 on 3 and 33 DF,  p-value: 0.02029
# Combo group
combo <- combo %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3) / 2,
    mid_band = (mean_lag_V4 + mean_lag_V5) / 2,
    high_band = (mean_lag_V6 + mean_lag_V7 + mean_lag_V8) / 3
  )
model_combo_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = combo)
summary(model_combo_simple)

Call:
lm(formula = mean_weekly_max_velocity ~ low_band + mid_band + 
    high_band, data = combo)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.86355 -0.34167  0.06995  0.46065  1.47691 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.794e+01  3.691e-01  48.606   <2e-16 ***
low_band    -7.850e-03  4.452e-03  -1.763   0.0877 .  
mid_band     2.258e-02  1.956e-02   1.154   0.2573    
high_band    2.952e-05  5.154e-02   0.001   0.9995    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.7945 on 31 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.1658,    Adjusted R-squared:  0.0851 
F-statistic: 2.054 on 3 and 31 DF,  p-value: 0.1267

Bigs model: Statistically significant overall and explains around 33% of the variation of max velocity. The low band is significantly negative. Every unit increase in the low band results in a decrease in max velocity. The mid and high bands are not significant but the high band shows a positive trend. Based on this model, for bigs, more low-band effort may reduce weekly top speed, possibly indicating underexposure of high speeds. High-band efforts might help, but aren’t clearly impactful in this group.

Skill model: Almost significant. Low band has a negative effect, mid band has a positive effect, and high band is not significant. Based on this model, for skill players, more low-intensity exposure seems detrimental to max speed, while moderate band (V5–V6) exposure may enhance it.

Combo model: Model is not significant. The combo position group contains many different type of positions (QBs, RBs, TEs, LBs) with different movement and velocities. This variability may obscure relationships.

Across groups, low-band exposure is consistently negatively related to peak weekly speed — suggesting that too much low-intensity work may reduce the capacity to reach high speeds.

Are relative efforts and bands more advantageous than the absolute bands provided?

Look at ID_11’s plot from the last question. This player is an offensive lineman. Looking at the plot, he is mostly in band 2 and 3 while never reaching bands 7 or 8 and rarely reaching band 6. Despite this, this athlete’s weekly max velocity is always at least 75% of their all-time max velocity. Only looking at the absolute bands that are provided, we might come to the conclusion that ID_11 is not achieving high running speeds, however after looking at his max velocity efforts relative to his all-time max velocity, he is reaching high running speeds.

Create Relative Bands for ID_11

# Create Relative Bands for ID_11

# Prepare ID_11 dataset with relative bands
ID_11 <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% 
  filter(anon_id == "ID_11", Maximum.Velocity != 0) %>%
  filter(!is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
    # Create Relative Bands
    relative_band = cut(
      pct_of_max,
      breaks = c(20, 40, 50, 60, 70, 80, 90, 100),
      labels = c("V2 (20-40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
      right = TRUE, # Set to TRUE to include 100% in band
      include.lowest = TRUE  # Includes 0 in first band
    ),
    # Reverse factor levels so highest band is on top in plot
  relative_band = factor(
    relative_band,
    levels = rev(c("V2 (20-40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
  )
  )

# Summarize counts per week and relative band
weekly_effort <- ID_11 %>%
  filter(!is.na(relative_band)) %>%
  group_by(week, relative_band) %>%
  summarise(count = n(), .groups = "drop") %>%
  group_by(week) %>%
  mutate(pct_effort = (count / sum(count)) * 100) %>%
  ungroup()

# Pivot wider to get percentages per band as columns
weekly_effort_wide <- weekly_effort %>%
  select(week, relative_band, pct_effort) %>%
  pivot_wider(names_from = relative_band, values_from = pct_effort, values_fill = 0)

# Calculate weekly max velocity % of player max velocity
weekly_max_velocity <- ID_11 %>%
  group_by(week) %>%
  summarise(pct_of_max_velocity = max(pct_of_max), .groups = "drop")

# Join the max velocity % back to wide effort data
weekly_effort_wide <- weekly_effort_wide %>%
  left_join(weekly_max_velocity, by = "week")

# Pivot longer for stacked bar plotting
weekly_effort_long <- weekly_effort_wide %>%
  pivot_longer(
    cols = c("V2 (20-40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
    names_to = "relative_band",
    values_to = "pct_effort"
  ) %>%
  mutate(
    relative_band = factor(
      relative_band,
      levels = rev(c("V2 (20-40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
    )
  )
# Plot Relative Bands for ID_11
ggplot(weekly_effort_long, aes(x = week, y = pct_effort, fill = relative_band)) +
  geom_col(position = "stack") +
  geom_line(aes(y = pct_of_max_velocity, group = 1), 
            color = "black", size = 1.2) +
  geom_point(aes(y = pct_of_max_velocity), 
             color = "black", size = 2) +
  scale_fill_manual(
    values = c(
      "V2 (20-40%)" = "darkgreen",
      "V3 (40-50%)" = "green2",
      "V4 (50-60%)" = "greenyellow",
      "V5 (60-70%)" = "yellow",
      "V6 (70-80%)" = "orange",
      "V7 (80-90%)" = "tomato",
      "V8 (90-100%)" = "darkred"
    )
  ) +
  labs(
    title = "Relative Velocity Band Effort % per Week, ID_11",
    x = "Week",
    y = "Percent of Weekly Efforts",
    fill = "Relative Velocity Band"
  ) + 
  scale_y_continuous(
    sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
  ) +
  theme_classic()

Comparing this with the absolute bands graph of ID_11, we can see that this is a much better representation of their running speeds.

Relative Bands for each athlete

# Function to create relative bands and plot these for each athlete

# Arrange data so that the plot will display in anon_id order
clean_anons <- Catapult_Session_clean %>%
  arrange(anon_id)

# Loop over all athletes and create their relative bands plots
unique(clean_anons$anon_id) %>%
  lapply(function(player_id) {
    # Prepare data for player
    player_data <- Catapult_Session_clean %>%
      filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
      filter(anon_id == player_id, Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
      mutate(
        week = floor_date(Date, unit = "week"),
        pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
        # Create Relative Bands
        relative_band = cut(
          pct_of_max,
          breaks = c(20, 40, 50, 60, 70, 80, 90, 100),
          labels = c("V2 (20-40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                     "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
          right = TRUE, # Set to true to include 100%
          include.lowest = TRUE  # Set to true to include 0 in 1st interval
        ),
        # Reverse factor levels so highest band is on top in plot
        relative_band = factor(
          relative_band,
          levels = rev(c("V2 (20-40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                         "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
        )
      )
    
    if (nrow(player_data) == 0) return(NULL)  # skip empty

    # Summarize counts per week and relative band
    weekly_effort <- player_data %>%
      filter(!is.na(relative_band)) %>%
      group_by(week, relative_band) %>%
      summarise(count = n(), .groups = "drop") %>%
      group_by(week) %>%
      mutate(pct_effort = (count / sum(count)) * 100) %>%
      ungroup()

    # Pivot wider to get percentages per band as columns
    weekly_effort_wide <- weekly_effort %>%
      select(week, relative_band, pct_effort) %>%
      pivot_wider(names_from = relative_band, values_from = pct_effort, values_fill = 0)

    # Calculate weekly max velocity % of player max velocity
    weekly_max_velocity <- player_data %>%
      group_by(week) %>%
      summarise(pct_of_max_velocity = max(pct_of_max), .groups = "drop")

    # Join
    weekly_effort_wide <- weekly_effort_wide %>%
      left_join(weekly_max_velocity, by = "week")
    
    # Pivot longer for stacked bar plotting
    weekly_effort_long <- weekly_effort_wide %>%
      pivot_longer(
          cols = -c(week, pct_of_max_velocity),
        names_to = "relative_band",
        values_to = "pct_effort"
      ) %>%
      mutate(
        relative_band = factor(
          relative_band,
          levels = rev(c("V2 (20-40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                         "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
        )
      )
    
    # Plot
    plot <- ggplot(weekly_effort_long, aes(x = week, y = pct_effort, fill = relative_band)) +
      geom_col(position = "stack") +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2 (20-40%)" = "darkgreen",
          "V3 (40-50%)" = "green2",
          "V4 (50-60%)" = "greenyellow",
          "V5 (60-70%)" = "yellow",
          "V6 (70-80%)" = "orange",
          "V7 (80-90%)" = "tomato",
          "V8 (90-100%)" = "darkred"
        )
      ) +
      scale_y_continuous(
        name = "Percent of Weekly Effort",
        sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
      ) +
      labs(
        title = paste("Relative Velocity Band Effort % and Max Velocity Trend for Player", player_id),
        x = "Week",
        fill = "Relative Velocity Band"
      ) +
      theme_classic()

    print(plot)
    
    return(NULL)
  })
[[1]]
NULL

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[5]]
NULL

[[6]]
NULL

[[7]]
NULL

[[8]]
NULL

[[9]]
NULL

[[10]]
NULL

[[11]]
NULL

[[12]]
NULL

[[13]]
NULL

[[14]]
NULL

[[15]]
NULL

[[16]]
NULL

[[17]]
NULL

[[18]]
NULL

[[19]]
NULL

[[20]]
NULL

[[21]]
NULL

[[22]]
NULL

[[23]]
NULL

[[24]]
NULL

[[25]]
NULL

[[26]]
NULL

[[27]]
NULL

[[28]]
NULL

[[29]]
NULL

[[30]]
NULL

[[31]]
NULL

[[32]]
NULL

[[33]]
NULL

[[34]]
NULL

[[35]]
NULL

[[36]]
NULL

[[37]]
NULL

[[38]]
NULL

[[39]]
NULL

[[40]]
NULL

[[41]]
NULL

[[42]]
NULL

[[43]]
NULL

[[44]]
NULL

[[45]]
NULL

[[46]]
NULL

[[47]]
NULL

[[48]]
NULL

[[49]]
NULL

[[50]]
NULL

[[51]]
NULL

[[52]]
NULL

[[53]]
NULL

[[54]]
NULL

[[55]]
NULL

[[56]]
NULL

[[57]]
NULL

[[58]]
NULL

[[59]]
NULL

[[60]]
NULL

[[61]]
NULL

[[62]]
NULL

[[63]]
NULL

[[64]]
NULL

[[65]]
NULL

[[66]]
NULL

[[67]]
NULL

[[68]]
NULL

[[69]]
NULL

[[70]]
NULL

[[71]]
NULL

[[72]]
NULL

[[73]]
NULL

[[74]]
NULL

[[75]]
NULL

[[76]]
NULL

[[77]]
NULL

[[78]]
NULL

[[79]]
NULL

[[80]]
NULL

[[81]]
NULL

[[82]]
NULL

[[83]]
NULL

[[84]]
NULL

[[85]]
NULL

[[86]]
NULL

[[87]]
NULL

[[88]]
NULL

[[89]]
NULL

[[90]]
NULL

[[91]]
NULL

[[92]]
NULL

[[93]]
NULL

[[94]]
NULL

[[95]]
NULL

[[96]]
NULL

[[97]]
NULL

[[98]]
NULL

[[99]]
NULL

[[100]]
NULL

[[101]]
NULL

[[102]]
NULL

[[103]]
NULL

[[104]]
NULL

# Create relative bands for all athletes

# Define the velocity band labels
band_levels <- c("V2 (20-40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                     "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)")

# Create exposure data frame for all players
weekly_sprint_exposure <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
    relative_band = cut(
      pct_of_max,
      breaks = c(20, 40, 50, 60, 70, 80, 90, 100.1),
      labels = band_levels,
      right = TRUE,  # will include 100
      include.lowest = TRUE
    ),
    relative_band = factor(relative_band, levels = band_levels)
  ) %>%
  filter(!is.na(relative_band)) %>%
  group_by(anon_id, week, relative_band) %>%
  summarise(effort_count = n(), .groups = "drop") %>%
  group_by(anon_id, week) %>%
  mutate(
    total_efforts = sum(effort_count),
    pct_effort = (effort_count / total_efforts) * 100
  ) %>%
  ungroup() %>%
  select(anon_id, week, relative_band, pct_effort) %>%
  pivot_wider(
    names_from = relative_band,
    values_from = pct_effort,
    values_fill = 0
  )

# Add weekly max velocity % of max
weekly_max_pct <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100
  ) %>%
  group_by(anon_id, week) %>%
  summarise(pct_of_max_velocity = max(pct_of_max, na.rm = TRUE), .groups = "drop")

# Final data set
relative_bands <- weekly_sprint_exposure %>%
  left_join(weekly_max_pct, by = c("anon_id", "week"))

# Convert to long for plotting
relative_bands_long <- relative_bands %>%
  pivot_longer(
    cols = starts_with("V"),
    names_to = "relative_band",
    values_to = "pct_effort"
  ) %>%
  mutate(
    relative_band = factor(relative_band, levels = band_levels)
  )
COMBO <- c("QB","LB","TE","RB", "ILB")
BIG <- c("OL", "DL", "DE", "DT")
SKILL <- c("WR", "DB", "CB", "SAF")
Positions <- c("COMBO", "BIG", "SKILL")

weekly_sprint_exposure <- left_join(weekly_sprint_exposure, player_positions, by = "anon_id", relationship = "many-to-many")

weekly_sprint_exposure <- weekly_sprint_exposure %>%
  mutate(Position = case_when(Primary.Position %in% COMBO ~ "COMBO",
                               Primary.Position %in% BIG ~ "BIG",
                               Primary.Position %in% SKILL ~ "SKILL"))

weekly_sprint_exposure <- distinct(weekly_sprint_exposure)

max_velocities <- weekly_velocity_efforts[,c("anon_id","week","pct_of_max_velocity")]

weekly_sprint_exposure <- left_join(weekly_sprint_exposure, max_velocities, by = c("anon_id", "week"), relationship="many-to-many")




sprint_exposure_big <- weekly_sprint_exposure %>%
  filter(Position == "BIG") %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (20-40%)`)) %>%
  ungroup() %>%
  na.omit()

sprint_exposure_combo <- weekly_sprint_exposure %>%
  filter(Position == "COMBO")  %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (20-40%)`)) %>%
  ungroup() %>%
  na.omit()

sprint_exposure_skill <- weekly_sprint_exposure %>%
  filter(Position == "SKILL")  %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (20-40%)`)) %>%
  ungroup() %>%
  na.omit()
  

When I built the models that considered the relative bands instead of the absolute bands provided, I found that initially, the results were a lot better than the previous models with the absolute bands. This suggests that the relative bounds which are segmented into 10% chunks of all time maximum velocity for each player is a lot more indicative of effort and therefore their percentage of maximum velocity in a given week. We are able to see though that there is a lot of multicollinearity within the relative bands calculated.

# Modeling

# Bigs Model
model_big <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
                data=sprint_exposure_big)
summary(model_big)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V3 + avg_V4 + avg_V5 + 
    avg_V6 + avg_V7 + avg_V8, data = sprint_exposure_big)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.3536 -2.0453 -0.4238  3.0271  7.3825 

Coefficients: (1 not defined because of singularities)
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 94.419490   2.672085  35.336  < 2e-16 ***
avg_V2       0.109035   0.042088   2.591  0.00979 ** 
avg_V3      -0.184292   0.031633  -5.826 8.80e-09 ***
avg_V4      -0.525727   0.065265  -8.055 3.59e-15 ***
avg_V5      -0.007007   0.026275  -0.267  0.78981    
avg_V6      -0.156410   0.026333  -5.940 4.57e-09 ***
avg_V7      -0.101020   0.051665  -1.955  0.05096 .  
avg_V8             NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.395 on 675 degrees of freedom
Multiple R-squared:  0.5617,    Adjusted R-squared:  0.5579 
F-statistic: 144.2 on 6 and 675 DF,  p-value: < 2.2e-16
# Skill Model
model_skill <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
                data=sprint_exposure_skill)
summary(model_skill)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V3 + avg_V4 + avg_V5 + 
    avg_V6 + avg_V7 + avg_V8, data = sprint_exposure_skill)

Residuals:
     Min       1Q   Median       3Q      Max 
-21.5539  -1.5243   0.8314   2.2824   4.5808 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 97.88743    1.00261  97.632  < 2e-16 ***
avg_V2      -0.31316    0.02476 -12.648  < 2e-16 ***
avg_V3       0.11433    0.04046   2.826  0.00485 ** 
avg_V4      -0.20598    0.02375  -8.672  < 2e-16 ***
avg_V5       0.07949    0.02860   2.779  0.00560 ** 
avg_V6      -0.17947    0.02233  -8.038 4.01e-15 ***
avg_V7      -0.12491    0.01580  -7.905 1.08e-14 ***
avg_V8            NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.474 on 684 degrees of freedom
Multiple R-squared:  0.295, Adjusted R-squared:  0.2888 
F-statistic:  47.7 on 6 and 684 DF,  p-value: < 2.2e-16
# Combo Model
model_combo <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
               data=sprint_exposure_combo)
summary(model_combo)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V3 + avg_V4 + avg_V5 + 
    avg_V6 + avg_V7 + avg_V8, data = sprint_exposure_combo)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.8241 -2.4566 -0.1927  2.0818  5.2847 

Coefficients: (1 not defined because of singularities)
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 106.96461    1.82898  58.483  < 2e-16 ***
avg_V2       -0.45039    0.03330 -13.526  < 2e-16 ***
avg_V3       -0.16937    0.04751  -3.565   0.0004 ***
avg_V4       -0.23402    0.03326  -7.036 6.71e-12 ***
avg_V5        0.05042    0.03436   1.467   0.1429    
avg_V6       -0.42216    0.02510 -16.818  < 2e-16 ***
avg_V7       -0.23244    0.02979  -7.802 3.72e-14 ***
avg_V8             NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.967 on 488 degrees of freedom
Multiple R-squared:  0.4771,    Adjusted R-squared:  0.4706 
F-statistic:  74.2 on 6 and 488 DF,  p-value: < 2.2e-16

Looking at the correlations between all of the predictors as well as the response variable in the model we can see that a lot of the predictors have super strong correlations which each other, some more so than they are with the response. This suggests that the issue with the models above is that multicollinearity is bogging them down and we are not seeing the true relationships between the predictors and the response.

cor(sprint_exposure_big[,13:20])
            avg_pct_max     avg_V8     avg_V7     avg_V6      avg_V5     avg_V4      avg_V3
avg_pct_max   1.0000000  0.4865222  0.6445407  0.3192494 -0.31031882 -0.6997373 -0.54003451
avg_V8        0.4865222  1.0000000  0.6952135  0.1432250 -0.48309810 -0.5621637 -0.77382752
avg_V7        0.6445407  0.6952135  1.0000000  0.5888598 -0.32557565 -0.9396666 -0.84733973
avg_V6        0.3192494  0.1432250  0.5888598  1.0000000  0.15143484 -0.6757628 -0.66059705
avg_V5       -0.3103188 -0.4830981 -0.3255756  0.1514348  1.00000000  0.3001174  0.06603157
avg_V4       -0.6997373 -0.5621637 -0.9396666 -0.6757628  0.30011740  1.0000000  0.78000601
avg_V3       -0.5400345 -0.7738275 -0.8473397 -0.6605971  0.06603157  0.7800060  1.00000000
avg_V2       -0.3574179 -0.6620986 -0.7855184 -0.6700407 -0.08266465  0.6539145  0.89174861
                 avg_V2
avg_pct_max -0.35741790
avg_V8      -0.66209858
avg_V7      -0.78551843
avg_V6      -0.67004071
avg_V5      -0.08266465
avg_V4       0.65391446
avg_V3       0.89174861
avg_V2       1.00000000
cor(sprint_exposure_skill[,13:20])
            avg_pct_max     avg_V8     avg_V7      avg_V6     avg_V5     avg_V4     avg_V3
avg_pct_max  1.00000000  0.3391430 -0.0781862  0.03905024  0.1254586 -0.2050365 -0.1191009
avg_V8       0.33914302  1.0000000  0.2663767 -0.24015312 -0.4838171 -0.6142101 -0.6069156
avg_V7      -0.07818620  0.2663767  1.0000000 -0.18801908 -0.7375266 -0.5562658 -0.6046100
avg_V6       0.03905024 -0.2401531 -0.1880191  1.00000000  0.4531227 -0.1796023 -0.2246013
avg_V5       0.12545861 -0.4838171 -0.7375266  0.45312268  1.0000000  0.3666557  0.3744037
avg_V4      -0.20503653 -0.6142101 -0.5562658 -0.17960230  0.3666557  1.0000000  0.6875933
avg_V3      -0.11910094 -0.6069156 -0.6046100 -0.22460135  0.3744037  0.6875933  1.0000000
avg_V2      -0.30920757 -0.4993300 -0.5159128 -0.31075378  0.2152760  0.4795015  0.6905731
                avg_V2
avg_pct_max -0.3092076
avg_V8      -0.4993300
avg_V7      -0.5159128
avg_V6      -0.3107538
avg_V5       0.2152760
avg_V4       0.4795015
avg_V3       0.6905731
avg_V2       1.0000000
cor(sprint_exposure_combo[,13:20])
            avg_pct_max      avg_V8     avg_V7      avg_V6      avg_V5     avg_V4     avg_V3
avg_pct_max   1.0000000  0.55044191  0.2831128 -0.11308822 -0.12549739 -0.3390832 -0.2777397
avg_V8        0.5504419  1.00000000  0.6982032  0.04492787 -0.66014337 -0.7494741 -0.6822165
avg_V7        0.2831128  0.69820315  1.0000000  0.27601693 -0.69690768 -0.8447070 -0.8649314
avg_V6       -0.1130882  0.04492787  0.2760169  1.00000000  0.08101263 -0.4838699 -0.5455592
avg_V5       -0.1254974 -0.66014337 -0.6969077  0.08101263  1.00000000  0.4734404  0.4645439
avg_V4       -0.3390832 -0.74947414 -0.8447070 -0.48386990  0.47344043  1.0000000  0.8406075
avg_V3       -0.2777397 -0.68221648 -0.8649314 -0.54555921  0.46454387  0.8406075  1.0000000
avg_V2       -0.3915264 -0.68029067 -0.7763047 -0.60519674  0.36233033  0.7936349  0.8353624
                avg_V2
avg_pct_max -0.3915264
avg_V8      -0.6802907
avg_V7      -0.7763047
avg_V6      -0.6051967
avg_V5       0.3623303
avg_V4       0.7936349
avg_V3       0.8353624
avg_V2       1.0000000

All of the models below are the best 3 predictor models that resulted for each position after running the best subsets algorithm. All of the following have reductions in adjusted-\(R^2\) but they don’t seem significant considering that we took out over half of the predictors and maintained a relatively similar adjusted-\(R^2\) value.

#Bigs

#best_sub_big <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_big, method="exhaustive")
#summary(best_sub_big)

model_big <- lm(avg_pct_max~avg_V2+avg_V6+avg_V7,
                data=sprint_exposure_big)
summary(model_big)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V6 + avg_V7, data = sprint_exposure_big)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.6953 -2.8762 -0.6379  2.3819  8.4553 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 72.52999    0.68993 105.126  < 2e-16 ***
avg_V2       0.19119    0.02311   8.272 6.96e-16 ***
avg_V6       0.01949    0.01961   0.994     0.32    
avg_V7       0.33232    0.01600  20.775  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.71 on 678 degrees of freedom
Multiple R-squared:  0.4741,    Adjusted R-squared:  0.4718 
F-statistic: 203.7 on 3 and 678 DF,  p-value: < 2.2e-16
#Skills

#best_sub_skill <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_skill, method="exhaustive")
#summary(best_sub_skill)

model_skill <- lm(avg_pct_max~avg_V2+avg_V5+avg_V6,
                data=sprint_exposure_skill)
summary(model_skill)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V5 + avg_V6, data = sprint_exposure_skill)

Residuals:
    Min      1Q  Median      3Q     Max 
-34.619  -1.565   0.184   2.381   5.526 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 90.35401    0.51443 175.641  < 2e-16 ***
avg_V2      -0.23848    0.02062 -11.565  < 2e-16 ***
avg_V5       0.17951    0.02236   8.029 4.28e-15 ***
avg_V6      -0.13557    0.02285  -5.934 4.70e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.746 on 687 degrees of freedom
Multiple R-squared:  0.1765,    Adjusted R-squared:  0.1729 
F-statistic: 49.07 on 3 and 687 DF,  p-value: < 2.2e-16
#Combos
#best_sub_combo <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_combo, method="exhaustive")
#summary(best_sub_combo)

model_combo <- lm(avg_pct_max~avg_V2+avg_V4+avg_V5,
               data=sprint_exposure_combo)
summary(model_combo)

Call:
lm(formula = avg_pct_max ~ avg_V2 + avg_V4 + avg_V5, data = sprint_exposure_combo)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.2418 -2.4661  0.6291  2.5272  6.9210 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 86.98522    0.50775 171.315  < 2e-16 ***
avg_V2      -0.15548    0.03217  -4.833  1.8e-06 ***
avg_V4      -0.04478    0.03349  -1.337    0.182    
avg_V5       0.02630    0.03136   0.839    0.402    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.757 on 491 degrees of freedom
Multiple R-squared:  0.1567,    Adjusted R-squared:  0.1515 
F-statistic: 30.41 on 3 and 491 DF,  p-value: < 2.2e-16

Since the models perform roughly the same with just 3 predictors as they do with all 7, it may make sense to instead truncate into more general bins. This may help us understand the relationship with low, medium and high effort with weekly maximum velocity.

sprint_exposure_big <- sprint_exposure_big %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)

sprint_exposure_skill <- sprint_exposure_skill %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)

sprint_exposure_combo <- sprint_exposure_combo %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)
#bigs
model_big_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_big)
summary(model_big_general)

Call:
lm(formula = avg_pct_max ~ avg_low + avg_medium + avg_high, data = sprint_exposure_big)

Residuals:
   Min     1Q Median     3Q    Max 
-8.632 -1.791 -0.790  2.369  8.653 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 91.70273    0.58548  156.63   <2e-16 ***
avg_low     -0.21289    0.01412  -15.08   <2e-16 ***
avg_medium  -0.50797    0.03495  -14.54   <2e-16 ***
avg_high          NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.959 on 679 degrees of freedom
Multiple R-squared:  0.4004,    Adjusted R-squared:  0.3987 
F-statistic: 226.7 on 2 and 679 DF,  p-value: < 2.2e-16
#skills
model_skill_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_skill)
summary(model_skill_general)

Call:
lm(formula = avg_pct_max ~ avg_low + avg_medium + avg_high, data = sprint_exposure_skill)

Residuals:
    Min      1Q  Median      3Q     Max 
-34.673  -1.718   0.784   1.955   7.801 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 88.90869    0.49464 179.743  < 2e-16 ***
avg_low     -0.17498    0.02524  -6.933 9.48e-12 ***
avg_medium   0.04499    0.02907   1.547    0.122    
avg_high          NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.987 on 688 degrees of freedom
Multiple R-squared:  0.06574,   Adjusted R-squared:  0.06302 
F-statistic:  24.2 on 2 and 688 DF,  p-value: 6.944e-11
#combos
model_combo_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_combo)
summary(model_combo_general)

Call:
lm(formula = avg_pct_max ~ avg_low + avg_medium + avg_high, data = sprint_exposure_combo)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.1658 -2.0159  0.3012  2.3565  7.2075 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 90.40529    0.66256 136.449  < 2e-16 ***
avg_low     -0.13407    0.02322  -5.773 1.38e-08 ***
avg_medium  -0.24183    0.04095  -5.906 6.56e-09 ***
avg_high          NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.694 on 492 degrees of freedom
Multiple R-squared:  0.1828,    Adjusted R-squared:  0.1795 
F-statistic: 55.03 on 2 and 492 DF,  p-value: < 2.2e-16

Still pretty bad, I am going to use principle components regression to see if there are other relationships we are missing

preds <- sprint_exposure_big[,14:20]

pc_loadings <- prcomp(preds, scale=TRUE)$rotation[,c(1,2,3)]
pc_loadings
              PC1         PC2        PC3
avg_V8  0.3589784 -0.39846525 -0.5094340
avg_V7  0.4469184 -0.08953137  0.1575945
avg_V6  0.3272167  0.45491322  0.4580859
avg_V5 -0.1106036  0.75421813 -0.3762755
avg_V4 -0.4230166  0.03282438 -0.4224436
avg_V3 -0.4468487 -0.08929811  0.2488676
avg_V2 -0.4165066 -0.21992488  0.3518826

How does sprinting exposure (# of efforts, % max reached) relate to incidence of hamstring injuries?

# Join relative_bands data set with injury data

Incident_dates <- Incident_Report_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01"))

# Get ids of athletes with injury
injured_ids <- unique(Incident_dates$anon_id)

# Filter relative_bands to only include athletes who got injured
injured_data <- relative_bands_long %>%
  filter(anon_id %in% injured_ids)

# Create injury weeks
injury_weeks <- Incident_Report_clean %>%
  mutate(
    week = floor_date(as.Date(Date.of.Injury), unit = "week")
  ) %>%
  filter(Date.of.Injury >= as.Date("2024-06-30") & Date.of.Injury <= as.Date("2025-07-01")) %>%
  select(anon_id, week) %>%
  distinct() %>%
  mutate(injury = 1)

injuries_and_running <- injured_data %>%
  left_join(injury_weeks, by = c("anon_id", "week")) %>%
  mutate(injury = ifelse(is.na(injury), 0, injury))

injuries_and_running <- injuries_and_running %>%
  mutate(relative_band = factor(relative_band, levels = band_levels))


# Filter to only include players 194 and 285
plot_data <- injuries_and_running %>%
  filter(anon_id %in% c("ID_194", "ID_285"))

# Identify injury weeks for shading
injury_shading <- plot_data %>%
  filter(injury == 1) %>%
  mutate(
    xmin = week - 3,
    xmax = week + 3,
    ymin = -Inf,
    ymax = Inf
  )

# Create a single faceted plot
ggplot(plot_data, aes(x = week)) +
  geom_rect(data = injury_shading,
            aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
            fill = "blue", alpha = 0.2, inherit.aes = FALSE) +
  geom_col(aes(y = pct_effort, fill = relative_band), position = position_stack(reverse = TRUE)) +
  geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
  geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
  facet_wrap(~ anon_id, scales = "free_x") +
  scale_fill_manual(
    values = c(
      "V2 (20-40%)" = "darkgreen",
      "V3 (40-50%)" = "green2",
      "V4 (50-60%)" = "greenyellow",
      "V5 (60-70%)" = "yellow",
      "V6 (70-80%)" = "orange",
      "V7 (80-90%)" = "tomato",
      "V8 (90-100%)" = "darkred"
    )
  ) +
  scale_y_continuous(
    name = "Percent of Weekly Effort",
    sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
  ) +
  labs(
    title = "Velocity Band Exposure & Max % for Injured Players 194 and 285",
    x = "Week",
    fill = "Relative Velocity Band"
  ) +
  theme_classic()

# Loop over injured athletes and create their plots

unique(injuries_and_running$anon_id) %>%
  lapply(function(player_id) {
    
    # Filter data for this player
    plot_data <- injuries_and_running %>%
      filter(anon_id == player_id)
    
    if (nrow(plot_data) == 0) return(NULL)  # Skip if no data
    
    # Get injury weeks
    injury_week_nums <- plot_data %>%
      filter(injury == 1) %>%
      pull(week)
    
    # Plot
    plot <- ggplot(plot_data, aes(x = week)) +
      # Highlight injury weeks with shaded blue areas
      geom_rect(data = data.frame(week = injury_week_nums),
                aes(xmin = week - 3, xmax = week + 3, ymin = -Inf, ymax = Inf),
                fill = "blue", alpha = 0.2, inherit.aes = FALSE) +
      geom_col(aes(y = pct_effort, fill = relative_band), position = position_stack(reverse = TRUE)) +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2 (20-40%)" = "darkgreen",
          "V3 (40-50%)" = "green2",
          "V4 (50-60%)" = "greenyellow",
          "V5 (60-70%)" = "yellow",
          "V6 (70-80%)" = "orange",
          "V7 (80-90%)" = "tomato",
          "V8 (90-100%)" = "darkred"
        )
      ) +
      scale_y_continuous(
        name = "Percent of Weekly Effort",
        sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
      ) +
      labs(
        title = paste("Velocity Band Exposure & Max % for Injured Player", player_id),
        x = "Week",
        fill = "Relative Velocity Band"
      ) +
      theme_classic()
    
    print(plot)
    
    return(NULL)
  })
[[1]]
NULL

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[5]]
NULL

[[6]]
NULL

[[7]]
NULL

[[8]]
NULL

[[9]]
NULL

[[10]]
NULL

[[11]]
NULL

[[12]]
NULL

[[13]]
NULL

[[14]]
NULL

[[15]]
NULL

The blue lines indicate a week where an injury occurred. No real insights can be made from looking at relative velocity bands and injury occurrances.

model_injury <- glm(injury ~ pct_of_max_velocity,
                    data = injuries_and_running,
                    family = "binomial")

summary(model_injury)

Call:
glm(formula = injury ~ pct_of_max_velocity, family = "binomial", 
    data = injuries_and_running)

Coefficients:
                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)         -3.71775    0.90526  -4.107 4.01e-05 ***
pct_of_max_velocity  0.00923    0.01041   0.887    0.375    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 939.85  on 2330  degrees of freedom
Residual deviance: 939.03  on 2329  degrees of freedom
AIC: 943.03

Number of Fisher Scoring iterations: 5

Simple model Each 1 percentage point increase in pct_of_max_velocity increases the log-odds of injury by 0.00923. Not significant.

# Get summary stats for both injury and non injury

# Clean and get date format
injuries_and_running_clean <- injuries_and_running %>%
  mutate(week_formatted = format(as.Date(week), "%m-%d-%Y")) %>%
  distinct(anon_id, week, .keep_all = TRUE)

# Filter for only injury weeks
injury_weeks <- injuries_and_running_clean %>%
  filter(injury == 1) %>%
  mutate(injury_event = paste0(anon_id, "__", week_formatted))

# Calculate statistics for injury weeks
injury_mean <- mean(injury_weeks$pct_of_max_velocity, na.rm = TRUE)

injury_summary_stats <- injury_weeks %>%
  summarise(
    mean_pct = mean(pct_of_max_velocity, na.rm = TRUE),
    sd_pct = sd(pct_of_max_velocity, na.rm = TRUE),
    n = sum(!is.na(pct_of_max_velocity))
  )
# Confidence interval 
se <- injury_summary_stats$sd_pct / sqrt(injury_summary_stats$n)
t_crit <- qt(0.975, df = injury_summary_stats$n - 1)
lower <- injury_summary_stats$mean_pct - t_crit * se
upper <- injury_summary_stats$mean_pct + t_crit * se
# Print
cat("95% CI for mean pct_of_max_velocity:", round(lower,2), "-", round(upper,2))
95% CI for mean pct_of_max_velocity: 82.52 - 90.59
# Filter for non-injury weeks
non_injury_weeks <- injuries_and_running_clean %>%
  filter(injury == 0)

# Get statistics for non-injury weeks
non_injury_mean <- mean(non_injury_weeks$pct_of_max_velocity, na.rm = TRUE)

non_injury_summary_stats <- non_injury_weeks %>%
  summarise(
    mean_pct = mean(pct_of_max_velocity, na.rm = TRUE),
    sd_pct = sd(pct_of_max_velocity, na.rm = TRUE),
    n = sum(!is.na(pct_of_max_velocity))
  )

# Confidence interval
non_injury_se <- non_injury_summary_stats$sd_pct / sqrt(non_injury_summary_stats$n)
non_injury_t_crit <- qt(0.975, df = non_injury_summary_stats$n - 1)
non_injury_lower <- non_injury_summary_stats$mean_pct - non_injury_t_crit * non_injury_se
non_injury_upper <- non_injury_summary_stats$mean_pct + non_injury_t_crit * non_injury_se

# Print results
cat("95% CI for mean pct_of_max_velocity (non-injury weeks):", 
    round(non_injury_lower, 2), "-", round(non_injury_upper, 2))
95% CI for mean pct_of_max_velocity (non-injury weeks): 84.65 - 86.83
# T-test to compare pct_of_max_velocity in injuries and non injuries that week
t_test <- t.test(
  pct_of_max_velocity ~ injury, 
  data = injuries_and_running_clean,
  var.equal = FALSE # use TRUE if you assume equal variances
)

# Print results
print(t_test)

    Welch Two Sample t-test

data:  pct_of_max_velocity by injury
t = -0.4103, df = 18.833, p-value = 0.6862
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
 -4.964421  3.337870
sample estimates:
mean in group 0 mean in group 1 
       85.73959        86.55287 

Group 1 (injury) mean = 86.55 Group 2 (non-injury) mean = 85.74 While the mean % of max velocity is higher for the injury group, the difference is small and not statistically significant. (high p-value, 0.69)

Plots of Percent of Max Velocity in that week

# Plot of % Max Velocity during injury and non injury weeks
ggplot(injuries_and_running_clean, aes(x = week, y = pct_of_max_velocity)) +
  geom_point(aes(color = factor(injury))) +
  scale_color_manual(values = c("0" = "black", "1" = "red"),
                     labels = c("No Injury", "Injury"),
                     name = "Injury Status") +
  labs(
    title = "Percent of Max Velocity by Week",
    subtitle = paste("Mean % of Max Velocity, Injury: ", round(injury_mean, 2), " |  Mean % of Max Velocity, Non-Injury: ", round(non_injury_mean, 2)),
    x = "Week",
    y = "% of Max Velocity Reached"
  ) +
  theme_classic()



ggplot(injury_weeks, aes(x = week, y = pct_of_max_velocity)) +
  geom_point(color = "red") +
  labs(
    title = "Percent of Max Velocity During Injury Weeks",
    subtitle = paste("% of Max Velocity Mean: ", round(injury_mean, 2)),
    x = "Week",
    y = "% of Max Velocity"
  ) +
  theme_classic()


# Create bar chart
ggplot(injury_weeks, aes(x = factor(injury_event), y = pct_of_max_velocity)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(pct_of_max_velocity, 1)),  # round to 1 decimal place
            vjust = -0.5, size = 2.75) +
  labs(
    title = "Percent of Max Velocity During Injury Weeks",
    subtitle = paste("Mean: ", round(injury_mean, 2)),
    x = "Injury Event",
    y = "% of Max Velocity"
  ) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Plot of % Max Velocity during injury and non injury weeks
ggplot(week_prior, aes(x = week, y = lag1_pct_of_max_velocity)) +
  geom_point(aes(color = factor(injury))) +
  scale_color_manual(values = c("0" = "black", "1" = "red"),
                     labels = c("No Injury", "Injury"),
                     name = "Injury Status") +
  labs(
    title = "Percent of Max Velocity of Prior Week",
    subtitle = paste("Mean % of Max Velocity, Injury: ", round(injury_week_prior_mean, 2), " |  Mean % of Max Velocity, Non-Injury: ", round(non_injury_week_prior_mean, 2)),
    x = "Week",
    y = "% of Max Velocity Reached"
  ) +
  theme_classic()


ggplot(injury_week_prior, aes(x = week, y = lag1_pct_of_max_velocity)) +
  geom_point(color = "red") +
  labs(
    title = "Percent of Max Velocity the week before injury",
    subtitle = paste("% of Max Velocity Mean: ", round(injury_week_prior_mean, 2)),
    x = "Week",
    y = "% of Max Velocity"
  ) +
  theme_classic()


# Bar chart for week prior to injury % Max
ggplot(injury_week_prior, aes(x = factor(injury_event), y = lag1_pct_of_max_velocity)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(lag1_pct_of_max_velocity, 1)),  # round to 1 decimal place
            vjust = -0.5, size = 2.75) +
  labs(
    title = "Percent of Max Velocity the Week Before Injury",
    subtitle = paste("% Max Mean: ", round(injury_week_prior_mean, 2)),
    x = "Injury Event",
    y = "% of Max Velocity"
  ) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# T-test to compare sum of current week, 1, 2, and 3 weeks before
t_test_sum_last3weeks <- t.test(
  sum_last3weeks ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_last3weeks)

    Welch Two Sample t-test

data:  sum_last3weeks by injury
t = -0.83663, df = 16.234, p-value = 0.4149
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
 -15.360139   6.659672
sample estimates:
mean in group 0 mean in group 1 
       343.5134        347.8636 

Mean % of max week before: injury = 86.6, non-injury = 86.2. Not sig, pvalue = 0.87

Mean % change week before (current week % - last week %): injury = -0.057, non-injury = -0.50 Not sig, p-value = 0.89

Mean % change of last 2 weeks (current - 2 weeks ago): injury = -2.68, non-injury = -0.66 Not sig, p-value = 0.44

Mean % change of last 3 weeks (current - 3 weeks ago): injury = -0.60, non-injury = -0.81. Not sig, p-value = 0.95

Sum of current, 1, 2, 3 weeks before: injury = 347.86, non-injury = 343.51 Not sig, p-value = 0.41

# Look into the last two weeks

# Drop NAs
lag_2_injury <- injury_week_prior %>%
  drop_na(lag2_pct_of_max_velocity)
lag_2_non_injury <- non_injury_week_prior %>%
  drop_na(lag2_pct_of_max_velocity)

# Calculate means
mean_lag2_injury <- mean(lag_2_injury$lag2_pct_of_max_velocity)
mean_lag2_non_injury <- mean(lag_2_non_injury$lag2_pct_of_max_velocity)
mean_lag2_injury
mean_lag2_non_injury

# Two weeks before:
# T-test to compare lag2_pct_of_max_velocity (2 weeks before) between injury and non injury
t_test_2week_prior <- t.test(
  lag2_pct_of_max_velocity ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_2week_prior)

# Change of last two weeks (lag1 - lag2)
t_test_change_weeks1_2 <- t.test(
  change_weeks1_2 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_weeks1_2)

# Sum of weeks 1 and 2
t_test_sum_weeks1_2 <- t.test(
  sum_weeks1_2 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_weeks1_2)


# 3 weeks before
# T-test to compare lag3_pct_of_max_velocity (3 weeks before) between injury and non injury
t_test_3week_prior <- t.test(
  lag3_pct_of_max_velocity ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_3week_prior)

# Change between 1 weeks before and 3 weeks before
t_test_change_weeks1_3 <- t.test(
  change_weeks1_3 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_weeks1_3)

# Sum of 1, 2, and 3 weeks before injury
t_test_sum_weeks1_2_3 <- t.test(
  sum_weeks1_2_3 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_weeks1_2_3)

2 weeks before injury: Means of % of max: injury = 88.7, non-injury = 85.8 Not a significant difference in means, p-value, 0.1833 > 0.05.

Means of change: injury = -2.34, non-injury = 0.108 Not significant (p-value = 0.47)

Mean of sum of weeks 1 and 2: injury = 175.14, non-injury = 171.72 Not significant, p-value = 0.333

3 weeks before injury: Means of % of max: injury = 86.7, non-injury = 85.9 Not a significant difference in means, high p-value, 0.7

Mean sum of 1, 2, and 3 weeks before injury: injury = 261.8, non-injury = 257.3 Not sig, p-value = 0.36

Mean change between 1 and 3 weeks before: injury = -0.26, non-injury = -0.27 Not sig, p-val = 0.99

model <- glm(injury ~ change_lastweek + change_last2weeks,
             data = week_prior, family = "binomial")
summary(model)

Call:
glm(formula = injury ~ change_lastweek + change_last2weeks, family = "binomial", 
    data = week_prior)

Coefficients:
                  Estimate Std. Error z value Pr(>|z|)    
(Intercept)       -2.99411    0.27524 -10.878   <2e-16 ***
change_lastweek    0.01958    0.02695   0.726    0.468    
change_last2weeks -0.02992    0.03072  -0.974    0.330    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 119.42  on 302  degrees of freedom
Residual deviance: 118.44  on 300  degrees of freedom
  (30 observations deleted due to missingness)
AIC: 124.44

Number of Fisher Scoring iterations: 6

There is no clear evidence that % of max velocity in the current or prior weeks (or their changes/sums) differs between injury and non-injury weeks, based on t-tests of mean differences.

Comparing the number of >90% efforts with injury
# Convert 90 percent max to logical
# Instead of Yes/No it is True/False
catapult_weekly <- Catapult_Session_clean %>%
  mutate(
    Date = as.Date(Date),
    week = floor_date(Date, unit = "week"),
    Hit90 = Hit.90.Percent.Max == "Yes"  # Convert to logical
  ) %>%
  distinct(anon_id, Date, .keep_all = TRUE)


# Get all combinations of player and week
all_combinations <- catapult_weekly %>%
  distinct(anon_id, week)

# Count weekly >90% sprint hits
weekly_hits <- catapult_weekly %>%
  group_by(anon_id, week) %>%
  summarise(
    count_90pct = sum(Hit90, na.rm = TRUE),
    .groups = "drop"
  )

# Join with all combinations to include zero counts
weekly_90pct <- all_combinations %>%
  left_join(weekly_hits, by = c("anon_id", "week")) %>%
  mutate(count_90pct = replace_na(count_90pct, 0))

# See breakdown of counts of # of times >90
distrubution <- weekly_90pct %>%
  count(count_90pct) %>%
  arrange(desc(n))
# Create injury weeks
injury_weeks <- Incident_Report_clean %>%
  mutate(
    week = floor_date(as.Date(Date.of.Injury), unit = "week")
  ) %>%
  filter(Date.of.Injury >= as.Date("2024-06-30") & Date.of.Injury <= as.Date("2025-07-01")) %>%
  select(anon_id, week) %>%
  distinct() %>%
  mutate(injury = 1)

# Merge with injury data to have column that indicates injury week
weekly_90pct_injuries <- weekly_90pct %>%
  left_join(injury_weeks, by = c("anon_id", "week")) %>%
  mutate(injury = replace_na(injury, 0))

# Calculate lagged >90% effort counts
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  arrange(anon_id, week) %>%
  group_by(anon_id) %>%
  mutate(
    lag1_90pct_count = lag(count_90pct, 1),
    lag2_90pct_count = lag(count_90pct, 2),
    lag3_90pct_count = lag(count_90pct, 3),
    
    change_lastweek = count_90pct - lag1_90pct_count,
    change_last2weeks = count_90pct - lag2_90pct_count,
    change_last3weeks = count_90pct - lag3_90pct_count,
    
    sum_last2weeks = count_90pct + lag1_90pct_count,
    sum_last3weeks = count_90pct + lag1_90pct_count + lag2_90pct_count,
    
    avg_lastweek = (count_90pct + lag1_90pct_count) / 2,
    avg_last2weeks = (count_90pct + lag1_90pct_count + lag2_90pct_count) / 3,
    avg_last3weeks = (count_90pct + lag1_90pct_count + lag2_90pct_count + lag3_90pct_count) / 4
  ) %>%
  ungroup()
# Bar chart of sprint counts the week of an injury

# Only injuries
injury_summary <- weekly_90pct_injuries %>%
  group_by(count_90pct) %>%
  summarise(
    total_injuries = sum(injury),
    total_weeks = n()
  )

ggplot(injury_summary, aes(x = factor(count_90pct), y = total_injuries)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = total_injuries), vjust = -0.3, size = 4) +
  labs(
    x = "Weekly >90% Sprint Count",
    y = "Total Injuries",
    title = "Injuries by >90% Sprint Count in the Week of Injury"
  ) +
  theme_classic()

# Injury rate table for week of injury counts
injury_rate_table <- weekly_90pct_injuries %>%
  group_by(count_90pct) %>%
  summarise(
    total_weeks = n(),                           # Total weeks with this count
    injury_weeks = sum(injury == 1),             # How many had injury that week
    injury_rate = injury_weeks / total_weeks    # Proportion injured
  ) %>%
  arrange(desc(injury_rate))
injury_rate_table
# Create a contingency table manually
injury_table <- matrix(c(
  10, 1343,   # Low: injured, not injured
  6, 395,     # Moderate: injured, not injured
  0, 78      # High: injured, not injured
), nrow = 3, byrow = TRUE)

# Add row and column names for clarity
rownames(injury_table) <- c("Low", "Moderate", "High")
colnames(injury_table) <- c("Injured", "Not_Injured")

# Run Fisher's Exact Test
fisher_result <- fisher.test(injury_table)

# View the result
print(fisher_result)

Our p-value of 0.33 means that there is no statically significance association between our sprint load groups and injury. Injury events are pretty rare overall in our dataset, making it hard to detect subtle differences.

# Summarize injuries and total weeks by total sprint counts over last 2 weeks
injury_summary_2week <- weekly_90pct_injuries %>%
  mutate(sum_last2weeks = replace_na(sum_last2weeks, 0)) %>%
  group_by(sum_last2weeks) %>%
  summarise(
    total_injuries = sum(injury == 1),
    total_weeks = n(),
    injury_rate = (total_injuries / total_weeks) * 100
  ) %>%
  arrange(desc(injury_rate))
injury_summary_2week

# Plot total injuries by total sprint counts over last 2 weeks
ggplot(injury_summary_2week, aes(x = factor(sum_last2weeks), y = total_injuries)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = total_injuries), vjust = -0.3, size = 4) +
  labs(
    x = "Total >90% Sprint Counts Over Last 2 Weeks",
    y = "Total Injuries",
    title = "Injuries by Total >90% Sprint Counts Over the Last Two Weeks"
  ) +
  theme_classic()


# Plot injury rate
ggplot(injury_summary_2week, aes(x=factor(sum_last2weeks), y = injury_rate)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(injury_rate, 3)), vjust = -0.3, size = 4) +
  labs(
    x = "Total >90% Sprint Counts Over Last 2 Weeks",
    y = "Injury Rate",
    title = "Injurie Rate by Total >90% Sprint Counts Over the Last Two Weeks"
  ) +
  theme_classic()

Current week counts: Injuries increase slightly from 0-3 counts with 3 having the highest injury rate of 2.0%. 4 and 5 counts have no injuries but a low sample size.

Counts over last few weeks (Grouped): High counts had no injuries Moderate counts had the highest injury rate (1.52%) Low counts had a injury rate of 0.74%

Injury rates increased from low counts (<1) injury rate of 0.74% to moderate counts (1-2) injury rate of 1.52% then dropped to 0% for high counts (2+). However, high counts have a small sample size (78), so the rate may be unstable.

weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(workload_group = case_when(
    avg2_group < 0.5 ~ "Low (<0.5)",
    avg2_group >= 0.5 ~ "High (>0.5)",
    TRUE ~ NA_character_
  ))

injury_rate_grouped <- weekly_90pct_injuries %>%
  group_by(workload_group) %>%
  summarise(
    total_weeks = n(),
    injuries = sum(injury == 1, na.rm = TRUE),
    injury_rate = (injuries / total_weeks) * 100
  )
injury_rate_grouped
NA
NA

Average over the last three weeks. The injury rate is higher when athletes average over 0.5 over 90 percent max sprint counts.

# Make the table
contingency <- matrix(c(8, 716, 8, 1084), nrow = 2, byrow = TRUE)
colnames(contingency) <- c("Injury", "NoInjury")
rownames(contingency) <- c("High", "Low")

# Run the test
chisq.test(contingency)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency
X-squared = 0.33061, df = 1, p-value = 0.5653
# Or for small expected counts:
fisher.test(contingency)

    Fisher's Exact Test for Count Data

data:  contingency
p-value = 0.4475
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
 0.4927592 4.6497160
sample estimates:
odds ratio 
   1.51357 

Since our p-value of 0.56>0.05, there is no statistically significant difference in injury rates between the High and Low >90 count groups. The chi-square test of the injury rates of average >90 counts of the last 3 weeks (either high > 0.5, or low < 0.5) showed no significant difference in injury rates between these groups.

It is important to note that the number of injury events observed in the dataset is relatively low. As a result, the statistical power to detect significant differences in injury rates between groups is limited. This low event count increases the likelihood of a Type II error (failing to detect a true effect), and thus, non-significant results should be interpreted with caution. Future studies with larger sample sizes or more injury occurrences are needed to better assess the relationship between workload and injury risk.

# T-tests:

# Current week
t.test(count_90pct ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Week before
t.test(lag1_90pct_count ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Change last week to this week
t.test(change_lastweek ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Change last two weeks
t.test(change_last2weeks ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Sum over last 2 weeks
t.test(sum_last2weeks ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(
    sprint_exposure_bin = ifelse(count_90pct > 0, "Some", "None")
  )

table_exposure <- table(weekly_90pct_injuries$sprint_exposure_bin, weekly_90pct_injuries$injury)

# Fisher's Exact Test
fisher.test(table_exposure)
# Create top quartile exposure
cutoff <- quantile(weekly_90pct_injuries$count_90pct, 0.75, na.rm = TRUE)

weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(
    sprint_exposure_group = ifelse(count_90pct >= cutoff, "High", "Low")
  )

table_quartile <- table(weekly_90pct_injuries$sprint_exposure_group, weekly_90pct_injuries$injury)

fisher.test(table_quartile)
glm(injury ~ count_90pct, data = weekly_90pct_injuries, family = "binomial")

glm(injury ~ lag1_90pct_count, data = weekly_90pct_injuries, family = "binomial")

change <- glm(injury ~ change_lastweek, data = weekly_90pct_injuries, family = "binomial")
summary(change)
glm(injury ~ change_last2weeks, data = weekly_90pct_injuries, family = "binomial")


sum <- glm(injury ~ sum_last3weeks, data = weekly_90pct_injuries, family = "binomial")
summary(sum)
multi_model <- glm(
  injury ~ count_90pct + sum_last2weeks,
  data = weekly_90pct_injuries,
  family = "binomial"
)

summary(multi_model)
ggplot(weekly_90pct_injuries, aes(x = change_lastweek, y = injury)) +
  geom_jitter(height = 0.05, alpha = 0.3, color = "blue") +  # Jitter to spread points vertically
  geom_smooth(method = "glm", method.args = list(family = "binomial"), se = TRUE, color = "red") +  # Logistic regression curve
  labs(
    title = "Probability of Injury vs Change in >90% Sprint Counts (Last Week)",
    x = "Change in >90% Sprint Counts (Current Week - Previous Week)",
    y = "Injury (0 = No, 1 = Yes)"
  ) +
  theme_minimal()

No t-tests or models revealed any insights.

#removing extra data
remove(band_long, bigs, clean_anons, combo, cor_data, cor_matrix, daily_90_counts,
       full_grid, hit_90_counts, ID_11, Incident_dates, injured_data, injuries_and_running,
       injury_by_group, injury_weeks, max_velocities, model_all_bands, model_big,
       model_bigs, model_combo, model_skill, player_counts_long, player_data,
       player_positions, plot_data, plots_by_position, position_averages,
       position_averages_with_team, pre_injury_weeks, QBs, relative_bands,
       relative_bands_long, skill, sprint_counts, sprint_exposure_big,
       sprint_exposure_combo, sprint_exposure_skill, sprint_injury_table, V8, 
       weekly_90_counts, weekly_band_effort_by_group, weekly_effort, weekly_effort_long,
       weekly_effort_wide, weekly_max_pct, weekly_max_velocity, weekly_sprint_counts,
       weekly_sprint_counts_lagged, weekly_sprint_exposure, weekly_velocity_efforts)

#removing extra values and functions
remove(all_athletes, all_weeks, band_levels, BIG, COMBO, SKILL, injured_ids,
       injury_week_nums, overall_avg, player_id, positions, Positions, qb_avg, team_avg,
       plot_hit_90_by_position)

Section 2: Running Imbalance

What is the variation at the team level and at each individual athlete level?

Looking at team data as a whole, since January 1, 2024 there is absolute no deviance from 0. That means that since January 1, 2024, the team has had the same average running imbalance of 0. This makes sense given that the team is so large and that imbalances can go from -100% to 100%. This suggests that throughout this time, at no point was there a team sway to one side. There also weren’t any points since January 1, 2024 that the team had any large spikes in average absolute value of running imbalance. This suggests that at no point throughout the season were there larger spikes than normal in running imbalance. Each player tends to have a very unique trend in their running imbalance. Looking at how the team varies but also at how each player varies throughout the season, it’s hard to make out any pattern that’s applicable to most people. The variance of running imbalance varies greatly between each player. Instead we looked at the variances between players who were injured and those who were not. Based on three different bootstrapped findings, we can see that the variances between players who were injured and those who were not were statistically significant.

For the first bootstrap, we compared the variance of the pooled groups meaning that the variance in running imbalance for players who were injured and those who were not were compared. This resulted in a 90% confidence interval which suggested that the difference in variance between the two groups is between 0.30 and 3.45. This suggests that when looking at the variance of the two groups separately but all the players are pooled together, the variances will most likely be different by factor between 0.30 and 3.45 and the variance for the injured pool will be greater than that of the uninjured pool. For the second bootstrap, each player’s variance was taken individually. This unpooled approach was taken to see if an individual player’s variance in running imbalance could potentially be related to HSI risk. The bootstrap algorithm in this case took the averaged variances of the bootstrapped sample for each group and compared them. This bootstrap produced a 90% confidence interval for the difference in average variance between the two groups is 0.79 to 1.32. These results suggest that players who sustained a hamstring injury since January 1, 2024 had, on average, a greater variance in their running imbalance by about 1.06. This suggests that there is a relationship between variance in running imbalance and HSI risk. This found increase in variability will be used to address the following questions. For the third bootstrap, we wanted to see if there was a difference in the average mean absolute value in running imbalance between players who were and weren’t injured. The bootstrapping algorithm for this test calculated the average absolute distance value or each running imbalance measurement and found the average for each sample. This test found that at the 90% significance level, injured players had an average running imbalance absolute value between 0.06 and 0.32 greater than their uninjured counterparts. These values though, when we consider that the range of running imbalance goes from 0 to 100 is small and may be hard to detect when out in the field.

We also looked at the relationship between running imbalance and higher level position. From this analysis we found that there doesn’t seem to be a super strong relationship between the three categories and average running imbalance variance. The bootstrap revealed that there are potentially significant differences between those who are Bigs and Combos. But, those who are Skills weren’t able to differentiate themselves between the two groups. Along with this, we looked at the average absolute value in running imbalance for the three groups. This analysis told us that while Combos and Bigs tended to have the same average absolute value running imbalance, Skills had a significantly higher average absolute running imbalance value. We can see from the very last chart in this analysis that Skills make up the most of those with hamstring injuries followed closely by Combos and Bigs making up around half of the amount of Skills. This is interesting considering that the amounts of Bigs, Skills, and Combos within the Historical Running data set are all roughly the same.

Team Analysis

#team variation
Historical_Running_clean %>%
  summarize(Team_Variation = var(Running.Imbalance))

#individual player variation
Historical_Running_clean %>%
  group_by(anon_id) %>%
  summarize(Player_Variation = var(na.omit(Running.Imbalance))) %>%
  ungroup() 

#average variance in running imbalance across all players
Historical_Running_clean %>%
  group_by(anon_id) %>%
  mutate(Player.var = var(na.omit(Running.Imbalance))) %>%
  ungroup() %>%
  summarize(Average_Player_Variance = mean(na.omit(Player.var)))

#making variance and average absolute value for each date to see trends
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(Date) %>%
  mutate(Date.Variance = var(na.omit(Running.Imbalance)),
         Date.Avg.Abs.Value = mean(abs(na.omit(Running.Imbalance)))) %>%
  ungroup()
#calculating mean and variance for team data
team_mean <- mean(Historical_Running_clean$Running.Imbalance)
team_sd <- sd(Historical_Running_clean$Running.Imbalance)

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_hline(yintercept = team_mean + team_sd) +
  geom_hline(yintercept = team_mean - team_sd) +
  geom_hline(yintercept = team_mean + (2*team_sd), color = "#A2A4A3") +
  geom_hline(yintercept = team_mean - (2*team_sd), color = "#A2A4A3") +
  geom_smooth(method = "lm", se = TRUE, color = "#CFB87C") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215") +
  theme_minimal()

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_smooth(method = "lm", se = TRUE, color = "#CFB87C") +
  geom_line(aes(x=Date, y=Date.Avg.Abs.Value), color = "#CFB87C") +
  geom_line(aes(x=Date, y=-Date.Avg.Abs.Value), color = "#CFB87C") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215") +
  theme_minimal()

#making histogram of team running imbalance data
ggplot(Historical_Running_clean, aes(Running.Imbalance)) +
  geom_histogram() +
  labs(title = "Team Running Imbalance Since January 1, 2024", x="Running Imbalance") +
  theme_minimal()

Injured Analysis

#making histogram for running imbalance of all injured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024") +
  theme_minimal()

#making histogram for running imbalance of all uninjured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "black", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024") +
  theme_minimal()

#Plotting injured and uninjured histograms over top one another
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(alpha = 0.75) +
  #adding in 95% CI for uninjured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.05)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.95)) +
  geom_histogram(data = Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance), fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% CI for injured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.05), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.95), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with and without HSI since January 1, 2024") +
  theme_minimal()
#making scatter plot of running imbalance data for injured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024") +
  theme_minimal()

#making scatter plot of running imbalance for uninjured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024") +
  theme_minimal()

Bootstrapping Differences between Injured and Uninjured Athletes

#Splitting up the data sets and calculating player variance and measurement absolute value
injured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()

uninjured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
#making a data frame to hold all of the within group variances
group_variances <- data.frame(injured_var = rep(NA,5000),
                uninjured_var = rep(NA,5000),
                diff_in_var = rep(NA, 5000))

#bootstrap for variance, 5000 iterations
for(i in 1:5000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  group_variances[i,1] = var(injured_sample$Running.Imbalance)
  group_variances[i,2] = var(uninjured_sample$Running.Imbalance)
  group_variances[i,3] = group_variances[i,1] - group_variances[i,2]
}
ggplot(data=group_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(x="Difference in Variance") +
  theme_minimal()

ggplot(data=group_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(x="Variance") +
  theme_minimal()
#making a data frame to hold all of the average player variances between groups
mean_player_variances <- data.frame(injured_var = rep(NA,5000),
                uninjured_var = rep(NA,5000),
                diff_in_var = rep(NA, 5000))

for(i in 1:5000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  mean_player_variances[i,1] = mean(na.omit(injured_sample$Player.Variance))
  mean_player_variances[i,2] = mean(na.omit(uninjured_sample$Player.Variance))
  mean_player_variances[i,3] = mean_player_variances[i,1] - mean_player_variances[i,2]
}
ggplot(data = mean_player_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(title = "Difference in Estimated Average Variance for Injured and Uninjured Athletes", x="Difference in Average Variance") +
  theme_minimal()

ggplot(data=mean_player_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(title = "Estimated Average Variance for Injured and Uninjured Athletes", x="Average Variance") +
  theme_minimal()
#making a data frame to hold all of the average absolute differences from 0
group_distance <- data.frame(injured_dist = rep(NA,5000),
                uninjured_dist = rep(NA,5000),
                diff_in_dist = rep(NA, 5000))

#bootstrap for variances, 5000 iterations
for(i in 1:5000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1658)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2405)
  
  #storing the calculated variances in data frame
  group_distance[i,1] = mean(injured_sample$Player.Absolute.Dist)
  group_distance[i,2] = mean(uninjured_sample$Player.Absolute.Dist)
  group_distance[i,3] = group_distance[i,1] - group_distance[i,2]
}
ggplot(data = group_distance, aes(diff_in_dist)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.95), color= "#CFB87C") +
  labs(title = "Estimated Difference in Average Absolute Value", x="Difference in Average Absolute Value") +
  theme_minimal()

ggplot(data = group_distance) +
  geom_histogram(aes(injured_dist), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_dist), alpha = 0.75) +
  labs(title = "Estimated Average Absolute Value", x="Average Absolute Value")  +
  theme_minimal()
#removing junk that came from the loops
remove(i,team_mean, team_sd, injured_sample, uninjured_sample, group_variances, injured_data, uninjured_data, mean_player_variances, group_distance)

Position Analysis

#making lists to sort position into larger categories
COMBO <- c("QB","LB","TE","RB", "ILB")
BIG <- c("OL", "DL", "DE", "DT")
SKILL <- c("WR", "DB", "CB", "SAF")
Positions <- c("COMBO", "BIG", "SKILL")

#giving positions to incident report
Incident_Report_clean <- Incident_Report_clean %>%
  mutate(Specific.Position = Position,
         Position = case_when(Specific.Position %in% COMBO ~ "COMBO",
                               Specific.Position %in% BIG ~ "BIG",
                               Specific.Position %in% SKILL ~ "SKILL"))

#giving positions to catapult session
Catapult_Session_clean <- Catapult_Session_clean %>%
  mutate(Specific.Position = Primary.Position,
         Position = case_when(Primary.Position %in% COMBO ~ "COMBO",
                               Primary.Position %in% BIG ~ "BIG",
                               Primary.Position %in% SKILL ~ "SKILL"))

#only taking IDs and position names and categories
incident_info <- Incident_Report_clean[,c("anon_id", "Position", "Specific.Position")]
catapult_info <- Catapult_Session_clean[,c("anon_id", "Position", "Specific.Position")]

#comprehensive list of IDs, their position, and category
info <- distinct(rbind(incident_info, catapult_info))

#add this only historical running
Historical_Running_clean <- left_join(Historical_Running_clean, info, by="anon_id",
                                      relationship="many-to-many") %>%
  #calculating each player's variance in running imbalance
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
for(i in 1:3){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$Position==Positions[i],],aes(Running.Imbalance)) +
    geom_histogram() +
    labs(subtitle=Positions[i])
  
  print(p)
}
Player_Summary_Stats <- distinct(Historical_Running_clean[,c("anon_id", "Position",
                                                 "Specific.Position", 
                                                 "Player.Variance")])
for(i in 1:3){
  p <- ggplot(data=Player_Summary_Stats[Player_Summary_Stats$Position==Positions[i],],
              aes(Player.Variance)) +
    geom_histogram() +
    labs(subtitle=Positions[i])
  
  print(p)
}

Bootstrapping Different Positions

#splitting up data set into the different categories
COMBOS <- Player_Summary_Stats %>%
  filter(Position == "COMBO") %>%
  na.omit()

SKILLS <- Player_Summary_Stats %>%
  filter(Position == "SKILL") %>%
  na.omit()

BIGS <- Player_Summary_Stats %>%
  filter(Position == "BIG") %>%
  na.omit()

group_avg_variance <- data.frame(COMBO_var = rep(NA, 5000),
                                 SKILL_var = rep(NA, 5000),
                                 BIG_var = rep(NA, 5000))
for(i in 1:5000){
  set.seed(i)
  combo_sample <- sample_n(COMBOS, size=20, replace=TRUE)
  skill_sample <- sample_n(SKILLS, size=22, replace=TRUE)
  big_sample <- sample_n(BIGS, size=24, replace=TRUE)
  
  group_avg_variance[i,1] <- mean(na.omit(combo_sample$Player.Variance))
  group_avg_variance[i,2] <- mean(na.omit(skill_sample$Player.Variance))
  group_avg_variance[i,3] <- mean(na.omit(big_sample$Player.Variance))
}
ggplot(data=group_avg_variance, aes(COMBO_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_avg_variance$COMBO_var, 0.05)) +
  geom_vline(xintercept = quantile(group_avg_variance$COMBO_var, 0.95)) +
  theme_minimal()

ggplot(data=group_avg_variance, aes(SKILL_var)) +
  geom_histogram(fill="#CFB87C") +
  geom_vline(xintercept = quantile(group_avg_variance$SKILL_var, 0.05), color="#CFB87C") +
  geom_vline(xintercept = quantile(group_avg_variance$SKILL_var, 0.95), color="#CFB87C") +
  theme_minimal()

ggplot(data=group_avg_variance, aes(BIG_var)) +
  geom_histogram(fill="#A2A4A3") +
  geom_vline(xintercept = quantile(group_avg_variance$BIG_var, 0.05), color="#A2A4A3") +
  geom_vline(xintercept = quantile(group_avg_variance$BIG_var, 0.95), color="#A2A4A3") +
  theme_minimal()

ggplot(data=group_avg_variance) +
  geom_histogram(aes(COMBO_var), alpha=0.5, fill="black") +
  geom_histogram(aes(SKILL_var), alpha=0.5, fill="#CFB87C") +
  geom_histogram(aes(BIG_var), alpha=0.5, fill="#A2A4A3") +
  labs(title="Estimated Average Variance in Running Imbalance", x="Average Variance in Running Imbalance") +
  theme_minimal()
var_confints <- data.frame(Category = c("COMBO", "SKILL", "BIG"),
                       Lower_Bound = c(quantile(group_avg_variance$COMBO_var, 0.05), 
                                       quantile(group_avg_variance$SKILL_var, 0.05), 
                                       quantile(group_avg_variance$BIG_var, 0.05)),
                       Median = c(quantile(group_avg_variance$COMBO_var, 0.5),
                                  quantile(group_avg_variance$SKILL_var, 0.5),
                                  quantile(group_avg_variance$BIG_var, 0.5)),
                       Upper_Bound = c(quantile(group_avg_variance$COMBO_var, 0.95),
                                       quantile(group_avg_variance$SKILL_var, 0.95),
                                       quantile(group_avg_variance$BIG_var, 0.95)))
head(var_confints)

ggplot(data=var_confints, aes(x=Category, y=Median, ymin=Lower_Bound, ymax=Upper_Bound, color=Category)) +
  geom_pointrange() +
  labs(y="Estimated Average Variance in Running Imbalance", title="Running Imbalance Variance 90% CI") +
  scale_color_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  theme_minimal()
Historical_Running_clean <- Historical_Running_clean %>%
  mutate(Abs.Value.Running.Imbalance = abs(Running.Imbalance))

COMBOS <- Historical_Running_clean %>%
  filter(Position == "COMBO")

SKILLS <- Historical_Running_clean %>%
  filter(Position == "SKILL")

BIGS <- Historical_Running_clean %>%
  filter(Position == "BIG")

group_avg_dist <- data.frame(COMBO_dist = rep(NA, 5000),
                             SKILL_dist = rep(NA, 5000),
                             BIG_dist = rep(NA, 5000))
for(i in 1:5000){
  set.seed(i)
  combo_sample <- sample_n(COMBOS, size=1203, replace=TRUE)
  skill_sample <- sample_n(SKILLS, size=1635, replace=TRUE)
  big_sample <- sample_n(BIGS, size=1224, replace=TRUE)
  
  group_avg_dist[i,1] <- mean(na.omit(combo_sample$Abs.Value.Running.Imbalance))
  group_avg_dist[i,2] <- mean(na.omit(skill_sample$Abs.Value.Running.Imbalance))
  group_avg_dist[i,3] <- mean(na.omit(big_sample$Abs.Value.Running.Imbalance))
}
ggplot(data=group_avg_dist) +
  geom_histogram(aes(COMBO_dist), alpha=0.5, fill="black") +
  geom_histogram(aes(SKILL_dist), alpha=0.5, fill="#CFB87C") +
  geom_histogram(aes(BIG_dist), alpha=0.5, fill="#A2A4A3") +
  labs(title = "Estimated Average Absolute Value Running Imbalance", x="Average Absolute Value Running Imbalance") +
  theme_minimal()
dist_confints <- data.frame(Category = c("COMBO", "SKILL", "BIG"),
                       Lower_Bound = c(quantile(group_avg_dist$COMBO_dist, 0.05), 
                                       quantile(group_avg_dist$SKILL_dist, 0.05), 
                                       quantile(group_avg_dist$BIG_dist, 0.05)),
                       Median = c(quantile(group_avg_dist$COMBO_dist, 0.5),
                                  quantile(group_avg_dist$SKILL_dist, 0.5),
                                  quantile(group_avg_dist$BIG_dist, 0.5)),
                       Upper_Bound = c(quantile(group_avg_dist$COMBO_dist, 0.95),
                                       quantile(group_avg_dist$SKILL_dist, 0.95),
                                       quantile(group_avg_dist$BIG_dist, 0.95)))
head(dist_confints)

ggplot(data=dist_confints, aes(x=Category, y=Median, ymin=Lower_Bound, ymax=Upper_Bound, color=Category)) +
  geom_pointrange() +
  scale_color_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  labs(title="Average Absolute Value Running Imbalance 90% CI", y="Estimated Average Absolute Value Running Imbalance") +
  theme_minimal()
Injury_Incidents <- distinct(Incident_Report_clean[,c("anon_id", "Position", "Date.of.Injury")])

Position_counts <- na.omit(distinct(Historical_Running_clean[,c("anon_id", "Position")]))

ggplot(Injury_Incidents, aes(Position, fill=Position)) +
  geom_bar() +
  scale_fill_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  labs(title="Hamstring Injuries by Position") +
  theme_minimal()

ggplot(Position_counts, aes(Position, fill=Position)) +
  geom_bar() +
  scale_fill_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  labs(title="Total Position Counts on Team") +
  theme_minimal()
remove(big_sample, BIGS, catapult_info, combo_sample, COMBOS, confints, group_avg_variance,
       incident_info, info, p, Player_Summary_Stats, skill_sample, SKILLS, BIG, COMBO, i,
       Positions, SKILL, dist_confints, group_avg_variance, group_avg_dist, group_distance, 
       group_variances, injured_data, injured_sample, Injury_Incidents, mean_player_variances, 
       Position_counts, var_confints)

What is a meaningful change? What red flags should go off when we see a week-to-week change in running imbalance?

Based on the analysis below, there doesn’t seem to be any major discernible differences in running imbalance before or following an injury. This suggests that there may not be a direct link between HSI risk and running imbalance value directly beforehand. Instead, the trends of many players who were injured seems to have a relatively consistent trend not entirely dependent on time. Looking at summary statistics of running imbalance in the weeks leading up to and following a hamstring injury, there are also no glaring trends. For this analysis, we looked at the mean and variance in running imbalance per week leading up to and after an injury for all of the injured players with running imbalance data. This showed us that there is no clear indicator of HSI risk in running imbalance or any summary statistic of it. Instead it may be more useful to look at each player’s total running imbalance and their individual variance. This seems to be more of a useful tool for differentiating between injured and uninjured athletes.

#getting running imbalances for just injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#making new column to represent when in time injury would be, negative means before injury and positive means after injury, 0 means date of injury if there's data for that day
Injured_Historical_Running$Weeks.After.Injury <- rep(NA, 1658)
Injured_Historical_Running$Injury.Count <- rep(NA, 1658)

#making new column in incident report for the injury count
Incident_Report_clean$Injury.Count <- rep(NA, 122)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through all of the dates in which the player had an injury
  for(j in 1:length(injury_dates)){
    
    #calculate dates for 1, 2, 3, 4 weeks before and after each injury date
    past_1 <- injury_dates[j]-7
    past_2 <- injury_dates[j]-14
    past_3 <- injury_dates[j]-21
    past_4 <- injury_dates[j]-28
    future_1 <- injury_dates[j]+7
    future_2 <- injury_dates[j]+14
    future_3 <- injury_dates[j]+21
    future_4 <- injury_dates[j]+28
    
    #Calculating how many injuries this is for the player
    injury_count <- as.character((length(injury_dates)) - j + 1)
    
    #compare date of data point for each player to date of injury, store in Weeks.After.Injury column, store injury count
    
    #first week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Weeks.After.Injury <- "1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Injury.Count <- injury_count
    
    #second week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Weeks.After.Injury <- "2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Injury.Count <- injury_count
    
    #third week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Weeks.After.Injury <- "3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Injury.Count <- injury_count
    
    #fourth week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Weeks.After.Injury <- "4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Injury.Count <- injury_count    
    #week right before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Weeks.After.Injury <- "-1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Injury.Count <- injury_count
    #two weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Weeks.After.Injury <- "-2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Injury.Count <- injury_count
    
    #three weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Weeks.After.Injury <- "-3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Injury.Count <- injury_count
        
    #four weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Weeks.After.Injury <- "-4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Injury.Count <- injury_count
    
    #Date of Injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Weeks.After.Injury <- "0"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Injury.Count <- injury_count
    
    
    #adding injury count to indicent report
    Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Injury.Count <- injury_count
    
  }
}


#making weeks after injury and injury count into a factor and combining data sets back together
Injured_Historical_Running <- Injured_Historical_Running %>%
  mutate(Weeks.After.Injury = factor(Weeks.After.Injury),
         Injury.Count = factor(Injury.Count))

Historical_Running_clean <- left_join(Historical_Running_clean, Injured_Historical_Running)

#getting rid of junk that was from the loop
remove(future_1, future_2, future_3, future_4, i, injury_count, injury_dates, j, past_1, past_2, past_3, past_4)
remove(Injured_Historical_Running)

Injury Risk

for(i in 1:22){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id==injured_IDs[i],], aes(Date, Running.Imbalance)) +
    geom_line(linetype=1) +
    geom_point(aes(color=Weeks.After.Injury)) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, linetype=2) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue")) +
    theme_minimal() +
    labs(title="Running Imbalance", subtitle = injured_IDs[i])
  
  print(p)
}
#making summary statistics of running imbalance per week relative to injury
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(anon_id, Injury.Count, Weeks.After.Injury) %>%
  mutate(Weeks.After.Injury.Variability = var(Running.Imbalance),
         Weeks.After.Injury.Mean = mean(abs(Running.Imbalance))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Mean, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Average Absolute Distance per Week",injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}
for(i in 1:22){
  #looking at variance in running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Variability, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Variance in Running Imbalance per Week", subtitle=injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}

Detectable Trend with Injury Risk

trends <- data.frame(ID = all_IDs,
                     KC = rep(0, 71))
for(i in 1:71){
  #only plotting if there are over 15 data points for each player
  if(nrow(Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],])>15){
    #calculating how strong of a non linear trend there is using a gam
    df <- summary(gam(Running.Imbalance~s(Days.Since.Start),
              data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]))$edf
    #calculating how strong of a linear trend there is using kendall correlation
    Kendall_Cor <- cor(x=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]$Days.Since.Start, y=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]$Running.Imbalance, method="kendall")
    
    trends[trends$ID == all_IDs[i],2] = Kendall_Cor
  
    if(all_IDs[i] %in% injured_IDs){
      p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],],
             aes(Date, Running.Imbalance)) +
        geom_point(color="skyblue") +
        geom_line(color="skyblue") +
        labs(subtitle=Kendall_Cor)
    }
    if(!(all_IDs[i] %in% injured_IDs)){
      p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],],
             aes(Date, Running.Imbalance)) +
        geom_point() +
        geom_line() +
        labs(subtitle=Kendall_Cor)
    }
    
    print(p)
  }
}

#plotted only from January 1, 2025 to May 1, 2025
trends <- trends %>%
  mutate(injured = ifelse(ID %in% injured_IDs,1,0))

ggplot(trends[trends$KC>0 & trends$injured==1,], aes(KC)) +
  geom_histogram()

ggplot(trends[trends$KC>0 & trends$injured==0,], aes(KC)) +
  geom_histogram()
injured_trends <- trends %>%
  filter(injured==1)

uninjured_trends <- trends %>%
  filter(injured==0)


kendall_cors <- data.frame(injured_avg_KC = rep(NA,5000),
                           uninjured_avg_KC = rep(NA,5000),
                           avg_diff_KC = rep(NA,5000))

for(i in 1:5000){
  set.seed(i)
  injured_samp <- sample_n(injured_trends, size=22, replace=TRUE)
  uninjured_samp <- sample_n(uninjured_trends, size=49, replace=TRUE)
  
  kendall_cors[i,1] <- mean(abs(injured_samp$KC))
  kendall_cors[i,2] <- mean(abs(uninjured_samp$KC))
  kendall_cors[i,3] <- kendall_cors[i,1] - kendall_cors[i,2]
}
ggplot(data=kendall_cors, aes(avg_diff_KC)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(kendall_cors$avg_diff_KC, 0.05), color = "#CFB87C") +
  geom_vline(xintercept = quantile(kendall_cors$avg_diff_KC, 0.95), color = "#CFB87C") +
  labs(title = "Estimated Difference in Average Kendall Rank Correlation Coefficient",
       x = "Difference in Average Kendall Coefficient")

ggplot(data=kendall_cors) +
  geom_histogram(aes(injured_avg_KC), alpha=0.7, fill="#CFB87C") +
  geom_histogram(aes(uninjured_avg_KC), alpha=0.7) +
  labs(title = "Estimated Average Kendall Rank Correlation Coefficient",
       x = "Average Kendall Coefficient")
remove(i, p, df, Kendall_Cor, kendall_cors, trends)

Is running imbalance sensitive enough of a metric to use as a prognosis tool versus a rehab tool?

Based on the analysis below, we can see that by solely using variance in running imbalance from the time of the injury to the end of the predicted return-to-play range is not a very strong predictor for whether or not it will take longer for a player to recover or not. In this analysis, we used the running imbalance in the time frame starting with the injury date to the end of the prognosis time frame. With these specific running imbalance values, we calculated the variance in running imbalance and whether or not the athlete returned to play within the predicted time frame or not. This analysis found that when using the variance in these time frames as the only predictor in a logistic regression model, the slope coefficient associated with the variance was statistically significant at the \(\alpha = 0.01\) significance level. With that though, the cross validated accuracy of the model was only around 0.6 suggesting that it wasn’t super strong in practice. In order to understand the impact that variance in running imbalance had on whether or not a player returned in the predicted time frame or not, we performed a bootstrap. We separated the observations in which players did and did not return in the predicted time frame into two different data sets. We then sampled from each of these two data sets and calculated the average variance for each sample. This was repeated 5000 times. This gave us an estimate of the average variance in running imbalance for players who returned within the predicted time frame and those who did not. This bootstrap revealed that players who did not return within the predicted time frame had a variance greater by roughly 1.2 during their time of recovery than those who returned on time. This tells us that while variance in running imbalance is not directly strong enough to predict whether or not an athlete will return within the predicted time frame, it can be used to supplement prognosis or make adjustments to the prognosis during the time of recovery of a HSI.

#Calculating date back to play in incident report data set
Incident_Report_clean <- Incident_Report_clean %>%
  filter(!is.na(Injury.Prognosis))%>%
  #calculating how long predicted time loss is based on prognosis
         #beginning of predicted range of return
  mutate(Expected.Start.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury,
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(7),
                                                      Date.of.Injury+days(28))))),
         #end of predicted range of return
         Expected.End.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury+days(7),
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(28),
                                                      Date.of.Injury+days(56)))))) %>%
  group_by(anon_id, Date.of.Injury) %>%
  #calculating actual date cleared to return
  mutate(Actual.Return = Date.of.Injury+days(sum(na.omit(Days.in.Status)))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Running.Imbalance)) +
  geom_line() +
    #Marking when injury occurred with gold line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, color="#CFB87C") +
    #Marking actual return date with solid brown line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Actual.Return, color="#8D7334", linetype=1) +
  #Marking beginning of predicted return to play range with grey dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return, color="#A2A4A3", linetype=3) +
    #Marking end of predicted return to play range with grey dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return, color="#A2A4A3", linetype=3) +
    labs(title=injured_IDs[i]) +
    annotate("rect", 
             xmin = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return,
             xmax = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return,
             ymin=min(Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],]$Running.Imbalance), ymax=max(Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],]$Running.Imbalance), alpha=0.3) +
    theme_minimal()
  
  print(p)
}
#looking at running imbalance of only injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#Making binary column if date was in time range of injury prognosis
Injured_Historical_Running$Date.in.Range <- rep(0, 1658)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through dates of injury for each player
  for(j in 1:length(injury_dates)){
    if(injured_IDs[i] == "ID_50"){ #ID_50 does not have enough running imbalance data
      break
    }
    #get the expected date of return for that instance of injury
    expected_return <- as.Date(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Expected.End.Return[1])
    
    #if the date in running imbalance is between day of injury and last day of prediction, set as 1
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] & Injured_Historical_Running$Date >= injury_dates[j] & Injured_Historical_Running$Date <= expected_return,]$Date.in.Range <- 1
  }
}

#making a column for the variance in running imbalance for each injury instance range
Injured_Historical_Running <- Injured_Historical_Running %>%
  filter(Date.in.Range == 1) %>%
  group_by(anon_id, Injury.Count) %>%
  mutate(Injury.Variability = var(Running.Imbalance)) %>%
  ungroup()

#adding injury and running data sets together
Injured_Data <- left_join(Injured_Historical_Running, Incident_Report_clean, by=c("anon_id", "Injury.Count"), relationship = "many-to-many") %>%
  #making new column if player returned in predicted time frame
  mutate(Return.in.Range = ifelse(Actual.Return>=Expected.Start.Return & Actual.Return <= Expected.End.Return, 1, 0)) %>%
  #removing rows that are missing important data
  filter(!is.na(Injury.Variability),
         !is.na(Return.in.Range))

Logistic Regression Model

set.seed(1000)
#making a 75% to 25% training to testing split
rows <- sample(1:nrow(Injured_Data), size=(nrow(Injured_Data)*0.75), replace=FALSE)
Injured_train <- Injured_Data[rows,]
Injured_test <- Injured_Data[-rows,]

#building logistic regression model from training data
return_to_play_model <- glm(Return.in.Range~Injury.Variability, data=Injured_train, family="binomial")

#looking at coefficients and p-values
summary(return_to_play_model)

#making predictions on testing data based off of the model built
Injured_test <- Injured_test %>%
  mutate(Prediction = ifelse(predict(return_to_play_model, newdata=Injured_test, type="response")>0.5, 1, 0))

#calculating CER
Injured_test %>%
  summarize(CER = mean(Prediction != Return.in.Range))
#making model with all of the data
return_to_play_cv <- glm(Return.in.Range~Injury.Variability, data=Injured_Data, family="binomial")

#making cost function for CER
cost <- function(obs, pred){
  mean((pred <= 0.5) & obs==1 | (pred > 0.5) & obs==0)
}

set.seed(1000)

#cross validating with K=10
ten_cv <- cv.glm(data=Injured_Data,glmfit=return_to_play_cv,cost,K=10)

#extract average error
ten_cv$delta[1]

Bootstrapping Differences Between In and Out of Range Return to Plays

#taking only players who recovered in predicted time frame
In_Range <- Injured_Data %>%
  filter(Return.in.Range == 1,
         !is.na(Injury.Variability))

#taking only players who did not recover in the predicted time frame
Out_Range <- Injured_Data %>%
  filter(Return.in.Range == 0,
         !is.na(Injury.Variability))
#making data frame to hold values
Range_Variances <- data.frame(in.avg.var = rep(NA, 5000),
                              out.avg.var = rep(NA, 5000),
                              diff.avg.var = rep(NA, 5000))

#bootstrapping 5000 times
for(i in 1:5000){
  set.seed(i)
  
  #sample from players who recovered in predicted range
  in_sample <- sample_n(In_Range, size=308, replace=TRUE)
  #sample fro players who did not recover in predicted range
  out_sample <- sample_n(Out_Range, size=477, replace=TRUE)
  
  
  #calculating variances of each sample and storing in data frame
  Range_Variances[i,1] <- mean(in_sample$Injury.Variability)
  Range_Variances[i,2] <- mean(out_sample$Injury.Variability)
  Range_Variances[i,3] <- Range_Variances[i,2] - Range_Variances[i,1] #diff in variances
}
#plotting differences in average variance
ggplot(data=Range_Variances, aes(diff.avg.var)) +
  geom_histogram() +
  #adding in 90% CI (does not include 0)
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.05), color ="#CFB87C") +
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.95), color ="#CFB87C") +
  labs(title = "Estimated Difference in Average Variance of Running Imbalance",
       x= "Difference in Average Variance Running Imbalance") +
  theme_minimal()

#plotting average variances of difference groups
ggplot(data=Range_Variances) +
  #players who recovered in predicted time frame
  geom_histogram(aes(in.avg.var), alpha = 0.75) +
  #players who did not recover in the predicted time frame
  geom_histogram(aes(out.avg.var), alpha = 0.75, fill ="#CFB87C") +
  labs(title = "Estimated Average Variance in Running Imbalance", 
       x="Average Variance in Running Imbalance") +
  theme_minimal()
remove(p,i,j,rows,ten_cv, cost, Injured_Data, Injured_Historical_Running, Injured_test, Injured_train, Range_Variances, return_to_play_cv, return_to_play_model, expected_return, injury_dates, In_Range, Out_Range, in_sample, out_sample)
LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNBdXRob3JzOiBJYW4gTWNFbHZlZW4gYW5kIENlY2lsaWEgR29uemFsZXMNCiNBdXRob3IgRGF0ZTogNy8xNC8yMDI1DQojUHVycG9zZTogVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBob3VzZSBhbGwgZGF0YSBzZXQgdHJhbnNmb3JtYXRpb24sIGNsZWFuc2luZywgdmlzdWFsaXphdGlvbiwgc3RhdGlzdGljYWwgYW5hbHlzaXMsIGFuZCBub3RlLXRha2luZyBmb3IgdGhlIDIwMjUgQ1UgQXRobGV0aWMgRGVwYXJ0bWVudCBTcG9ydHMgU2NpZW5jZSBJbnRlcm5zaGlwIFByb2dyYW0NCg0KI0xBU1QgVVBEQVRFRDogOC81LzIwMjUNCg0KI0luY2x1ZGluZyBoZWxwZnVsIGxpYnJhcmllcw0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoYW9kKQ0KbGlicmFyeShndCkNCmxpYnJhcnkoYm9vdCkNCmxpYnJhcnkobWdjdikNCmxpYnJhcnkobG1lNCkNCmxpYnJhcnkobGVhcHMpDQpgYGANCg0KDQojIERhdGEgQ2xlYW5pbmcNCmBgYHtyIExvYWRpbmcgaW4gdGhlIGRhdGEgc2V0c30NCiNsb2FkaW5nIGluIHRoZSBDYXRhcHVsdCBkYXRhIHRvIGxvb2sgYXQgc3ByaW50aW5nIHZhbHVlcw0KQ2F0YXB1bHRfU2Vzc2lvbiA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0NhdGFwdWx0IFNlc3Npb24gLSBPdXRkb29yIEZCLmNzdiIpDQoNCiNsb2FkaW5nIGluIHRoZSBIaXN0b3JpY2FsIFJ1bm5pbmcgZGF0YSB0byBsb29rIGF0IHJ1bm5pbmcgaW1iYWxhbmNlIHZhbHVlcw0KSGlzdG9yaWNhbF9SdW5uaW5nIDwtIHJlYWRfY3N2KCJkYXRhLXNldHMvZGF0YS1zZXRzLXVuY29tcHJlc3NlZC9kYXRhLXNldHMtY29tcHJlc3NlZC9SdW5uaW5nIEltYmFsYW5jZSBhbmQgU3BlZWQvQ29tcGlsZWQgSGlzdG9yaWNhbCBSdW5uaW5nIEltYmFsYW5jZSBGQi5jc3YiKQ0KDQojbG9hZGluZyBpbiB0aGUgSW5jaWRlbnQgUmVwb3J0IHRvIGxvb2sgYXQgSFNJcw0KSW5jaWRlbnRfUmVwb3J0IDwtIHJlYWRfY3N2KCJkYXRhLXNldHMvZGF0YS1zZXRzLXVuY29tcHJlc3NlZC9kYXRhLXNldHMtY29tcHJlc3NlZC9SdW5uaW5nIEltYmFsYW5jZSBhbmQgU3BlZWQvSW5jaWRlbnQgUmVwb3J0IEZCIElEcy5jc3YiKQ0KDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgQ2F0YXB1bHRfU2Vzc2lvbn0NCkNhdGFwdWx0X1Nlc3Npb25fY2xlYW4gPC0gQ2F0YXB1bHRfU2Vzc2lvbiAlPiUNCiAgI3B1dHRpbmcgdGhlIGRhdGUgYXMgYSBkYXRlIGNsYXNzDQogIG11dGF0ZShEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHNlbGVjdGluZyBpbXBvcnRhbnQgY29sdW1ucyBmb3IgdGhpcyBhbmFseXNpcw0KICBzZWxlY3QoYW5vbl9pZCwgRGF0ZSwgQWdlLCBQcmltYXJ5LlBvc2l0aW9uLCBUb3RhbC5EaXN0YW5jZSwgUGVyaW9kLk5hbWUsIFRvdGFsLkR1cmF0aW9uLi5taW4uLCBWZWxvY2l0eS5CYW5kLjEuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuMi5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC4zLlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjQuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuNS5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC42LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjcuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuOC5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC4yLlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC4zLlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC40LlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC41LlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC42LlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC43LlRvdGFsLkVmZm9ydC5Db3VudCwgVmVsb2NpdHkuQmFuZC44LlRvdGFsLkVmZm9ydC5Db3VudCwgTWF4aW11bS5WZWxvY2l0eSwgQXZlcmFnZS5WZWxvY2l0eSwgSGl0LjkwLlBlcmNlbnQuTWF4LCBEYXRlLm9mLkxhc3QuOTAuRWZmb3J0LCBEYXlzLlNpbmNlLkxhc3QuOTAuRWZmb3J0LCBIaXQuTWF4LlZlbG9jaXR5LiwgRGF0ZS5vZi5BbGwuVGltZS5NYXguVmVsb2NpdHksIERheXMuU2luY2UuTWF4LlZlbG9jaXR5LCBTZXNzaW9uLk1heC5WZWxvY2l0eSkgJT4lDQogICNjYWxjdWxhdGluZyBlYWNoIHBsYXllcidzIG1heGltdW0gdmVsb2NpdHkNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuTWF4LlZlbG9jaXR5ID0gbWF4KG5hLm9taXQoTWF4aW11bS5WZWxvY2l0eSkpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgZGF0YSBmcm9tIEphbnVhcnkgMSwgMjAyNCBhbmQgb24NCiAgZmlsdGVyKERhdGUgPj0gIjIwMjQtMDEtMDEiKQ0KDQpoZWFkKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4pDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgSGlzdG9yaWNhbF9SdW5uaW5nfQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZyAlPiUNCiAgI3Rha2luZyBvdXQgcm93cyB0aGF0IGRvbid0IGhhdmUgZGF0YQ0KICBmaWx0ZXIoUnVubmluZy5JbWJhbGFuY2UgIT0gIm4vYSIpICU+JQ0KICAjcHV0dGluZyBydW5uaW5nIGltYmFsYW5jZSBhcyBhIG51bWJlciBhbmQgY29udmVydGluZyB0aGUgZGF0ZSB0byBhIGRhdGUgY2xhc3MNCiAgbXV0YXRlKFJ1bm5pbmcuSW1iYWxhbmNlID0gYXMubnVtZXJpYyhSdW5uaW5nLkltYmFsYW5jZSksDQogICAgICAgICBEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHVzaW5nIGRhdGEgZnJvbSBKYW51YXJ5IDEsIDIwMjQgYW5kIG9uDQogIGZpbHRlcihEYXRlID49ICIyMDI0LTAxLTAxIikgJT4lDQogIG11dGF0ZShYPTE6NDA2MykgJT4lDQogICNtYWtpbmcgZGF5cyBzaW5jZSBKYW51YXJ5IDEsIDIwMjQgZm9yIGVhY2ggcGxheWVyDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoRGF5cy5TaW5jZS5TdGFydCA9IGFzLm51bWVyaWMoRGF0ZSAtIG1pbihEYXRlKSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KaGVhZChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4pDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgSW5jaWRlbnRfUmVwb3J0fQ0KSW5jaWRlbnRfUmVwb3J0X2NsZWFuIDwtIEluY2lkZW50X1JlcG9ydCAlPiUNCiAgI2ZpbHRlcmluZyBmb3Igb25seSBoYW1zdHJpbmcgaW5qdXJpZXMNCiAgZmlsdGVyKE9TSUNTMTQuQ29kZSA9PSAiVE0xIiwNCiAgICAgICAgIFN0YXR1cyAhPSAiRnVsbCBHbyIpICAlPiUNCiAgI2dldHRpbmcgdGhlIGRhdGUgb2YgdGhlIGluanVyeSBhcyBhIGRhdGUgY2xhc3MNCiAgbXV0YXRlKERhdGUgPSBhcy5EYXRlKERhdGUsICIlbS8lZC8lWSIpLA0KICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkgPSBhcy5EYXRlKERhdGUub2YuSW5qdXJ5Li4uT25zZXQub2Yuc3ltcHRvbXMsICIlbS8lZC8lWSIpLA0KICAgICAgICAgRXhhbWluYXRpb24uRGF0ZSA9IGFzLkRhdGUoRXhhbWluYXRpb24uRGF0ZSwgIiVtLyVkLyVZIikpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgcmVsZXZhbnQgY29sdW1ucyBmb3IgdGhpcyBhbmFseXNpcw0KICBzZWxlY3QoYW5vbl9pZCwgUG9zaXRpb24sIERhdGUsIERhdGUub2YuSW5qdXJ5LCBUaW1lLm9mLkluanVyeSwgU2lkZSwgT1NJQ1MuSW5qdXJ5LkRpYWdub3NpcywgQ29hY2gucy5EaWFnbm9zaXMsIFJlY3VycmVuY2Uub2YuSW5qdXJ5LCBDaG9vc2UuU2Vhc29uLCBPbnNldC5vZi5TeW1wdG9tcywgSW5qdXJ5LlByb2dub3NpcywgR2VuZXJhbC5NZWNoYW5pc20sIFNwZWNpZmljLk1lY2hhbmlzbSwgSW5qdXJlZC5XaGlsZS4sIFR5cGUub2YuRXZlbnQsIFNlYXNvbi4sIFN0YXR1cywgRGF5cy5pbi5TdGF0dXMpICU+JQ0KICAjbWFraW5nIGRheXMgb3V0IGR1ZSB0byBpbmp1cnkgZm9yIGVhY2ggcGxheWVyIGFuZCBlYWNoIGluanVyeSB0aGV5IHN1c3RhaW5lZA0KICBncm91cF9ieShhbm9uX2lkLCBEYXRlLm9mLkluanVyeSkgJT4lDQogIG11dGF0ZShEYXlzLk91dCA9IHN1bShEYXlzLmluLlN0YXR1cykpICU+JQ0KICB1bmdyb3VwKCkNCg0KaGVhZChJbmNpZGVudF9SZXBvcnRfY2xlYW4pDQpgYGANCg0KYGBge3IgSWRlbnRpZnlpbmcgcGxheWVycyB0aGF0IGhhdmUgZGF0YSBpbiBib3RoIGRhdGEgc2V0c30NCiN0YWtpbmcgdGhlIElEcyBvZiBwbGF5ZXJzIHdobyBhcmUgYW5kIGFyZW4ndCBpbmp1cmVkDQphbGxfSURzIDwtIHVuaXF1ZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCkNCiN0YWtpbmcgSURzIHRoYXQgd2VyZSBpbmp1cmVkIGFuZCBhbHNvIGhhdmUgcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KaW5qdXJlZF9JRHMgPC0gaW50ZXJzZWN0KHVuaXF1ZShJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZCksIGFsbF9JRHMpDQojdGFraW5nIGFsbCBwbGF5ZXJzIHdpdGggcnVubmluZyBpbWJhbGFuY2UgZGF0YSB0aGF0IGRvbid0IGhhdmUgYW4gaW5qdXJ5DQp1bmluanVyZWRfSURzIDwtIHVuaXF1ZSgoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoIWFub25faWQgJWluJSBpbmp1cmVkX0lEcykpJGFub25faWQpDQpgYGANCg0KYGBge3IgRmlsdGVyaW5nIG91dCBwbGF5ZXJzIHdpdGhvdXQgcHJvcGVyIGRhdGEgZnJvbSBhbGwgZGF0YSBzZXRzfQ0KI2luanVyZWQgcGxheWVycyB3aG8gYWxzbyBoYXZlIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGENCkluY2lkZW50X1JlcG9ydF9jbGVhbiA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMpDQoNCiNhbGwgcGxheWVycyB0aGF0IG9ubHkgaGF2ZSBydW5uaW5nIGltYmFsYW5jZSBkYXRhIG9yIGhhdmUgYm90aCBydW5uaW5nIGltYmFsYW5jZSBkYXRhIGFuZCBpbmNpZGVuY2UgcmVwb3J0DQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCAlaW4lIGluanVyZWRfSURzIHwgYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMpDQpgYGANCg0KYGBge3IgUmVtb3ZpbmcgYWxsIHVuaW1wb3J0YW50IG9iamVjdHMgY3JlYXRlZCBkdXJpbmcgY2xlYW5pbmd9DQojcmVtb3ZpbmcgdW5jbGVhbmVkIGRhdGEgc2V0cw0KcmVtb3ZlKEluY2lkZW50X1JlcG9ydCkNCnJlbW92ZShIaXN0b3JpY2FsX1J1bm5pbmcpDQpyZW1vdmUoQ2F0YXB1bHRfU2Vzc2lvbikNCmBgYA0KDQojIFNlY3Rpb24gMTogUnVubmluZyBTcGVlZA0KDQojIyBIb3cgb2Z0ZW4gYXJlIGF0aGxldGVzIHJlYWNoaW5nIOKJpSA5MCUgbWF4aW11bSB2ZWxvY2l0eSB0aHJvdWdob3V0IGEgdHJhaW5pbmcgc2Vhc29uPw0KDQpgYGB7cn0NCiMgQmFyIGNoYXJ0IGZvciBob3cgb2Z0ZW4gcGxheWVycyByZWFjaCDiiaUgOTAlIG1heGltdW0gdmVsb2NpdHkNCg0KIyBDb3VudCBmb3IgaG93IG1hbnkgdGltZXMgZWFjaCBhbm9uX2lkIGhpdCDiiaUgOTAlIG1heGltdW0gdmVsb2NpdHkNCmhpdF85MF9jb3VudHMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JSAjIEZpbHRlciBmb3IgdHJhaW5pbmcgc2Vhc29uDQogIGZpbHRlcihIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIpICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCBEYXRlLCBQcmltYXJ5LlBvc2l0aW9uKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgUHJpbWFyeS5Qb3NpdGlvbikgJT4lDQogIHN1bW1hcmlzZSh0aW1lc19oaXRfOTAgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUoDQogICAgUG9zaXRpb25fR3JvdXAgPSBjYXNlX3doZW4oDQogICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgYygiUUIiLCAiTEIiLCAiVEUiLCAiUkIiKSB+ICJDT01CTyIsDQogICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgYygiT0wiLCAiREwiLCAiREUiKSB+ICJCSUdTIiwNCiAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBjKCJXUiIsICJEQiIsICJEQiwgV1IiKSB+ICJTS0lMTCIsDQogICAgICBUUlVFIH4gIk9USEVSIg0KICAgICkpDQoNCiMgUGxvdCBvZiBhbGwgcGxheWVycycgZnJlcXVlbmNpZXMNCmdncGxvdChoaXRfOTBfY291bnRzLCBhZXMoeCA9IGFub25faWQsIHkgPSB0aW1lc19oaXRfOTApKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oaGl0XzkwX2NvdW50cyR0aW1lc19oaXRfOTApLCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjNTY1QTVDIiwgbGluZXdpZHRoID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiUGxheWVyIENvdW50cyBmb3IgQWNoaWV2aW5nIOKJpSA5MCUgb2YgTWF4aW11bSBWZWxvY2l0eSBEdXJpbmcgMjAyNOKAkzI1IFNlYXNvbiIsIA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIlRlYW0gQXZlcmFnZToiLCByb3VuZChtZWFuKGhpdF85MF9jb3VudHMkdGltZXNfaGl0XzkwKSwgMSkpLA0KICAgICAgIHggPSAiQXRobGV0ZSBJRCIsIHkgPSAiVGltZXMg4omlIDkwJSIpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIGFuZ2xlID0gOTApKQ0KYGBgDQojIERvIG5vdCBuZWVkIGJlY2F1c2Ugd2UgY3JlYXRlZCBhIGZ1bmN0aW9uIHRvIHBsb3QgZWFjaCBwb3NpdGlvbg0KYGBge3J9DQojIEJhciBjaGFydCBvZiB0aW1lcyByZWFjaGVkID45MCUsIFF1YXJ0ZXJiYWNrcw0KDQojIEZpbHRlciB0byBvbmx5IGhhdmUgZGF0YSBmb3IgUUJzDQpRQnMgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgZmlsdGVyKFByaW1hcnkuUG9zaXRpb24gPT0gIlFCIikNCg0KIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2VzIGZpcnN0DQpvdmVyYWxsX2F2ZyA8LSBtZWFuKGhpdF85MF9jb3VudHMkdGltZXNfaGl0XzkwKQ0KcWJfYXZnIDwtIG1lYW4oUUJzJHRpbWVzX2hpdF85MCkNCg0KZ2dwbG90KFFCcywgYWVzKHg9YW5vbl9pZCwgeT10aW1lc19oaXRfOTApKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbCA9ICIjQ0ZCODdDIikgKyANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRpbWVzX2hpdF85MCksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCANCiAgICAgICAgICAgIHNpemUgPSAzLjUpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gb3ZlcmFsbF9hdmcsIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHFiX2F2ZywgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzU2NUE1QyIpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IG92ZXJhbGxfYXZnICsgMC41LCANCiAgICAgICAgICAgbGFiZWwgPSAiVGVhbSBBdmciLCBjb2xvciA9ICIjMDAwMDAwIiwgc2l6ZSA9IDMpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IHFiX2F2ZyArIDAuNSwgDQogICAgICAgICAgIGxhYmVsID0gIlFCIEF2ZyIsIGNvbG9yID0gIiM1NjVBNUMiLCBzaXplID0gMykgKw0KICBsYWJzKHRpdGxlID0gIlFCIENvdW50cyBmb3IgUmVhY2hpbmcg4omlOTAlIE1heCBWZWxvY2l0eSIsIA0KICAgICAgIHggPSAiQXRobGV0ZSBJRCIsIHkgPSAiVGltZXMg4omlOTAlIiwNCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJRQiBBdmVyYWdlOiIsIHFiX2F2ZykpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQojIyBGYWNldCBwbG90IHVzZWZ1bCwgYnV0IGhhcmQgdG8gcmVhZCBzbyBwcm9iYWJseSBkb24ndCBpbmNsdWRlIGluIHByZXNlbnRhdGlvbi4NCmBgYHtyfQ0KIyBGYWNldCBQbG90DQoNCiMgQ2FsY3VsYXRlIG92ZXJhbGwgYXZlcmFnZQ0Kb3ZlcmFsbF9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCg0KIyBQbG90IGZhY2V0ZWQgYmFyIGNoYXJ0cyBieSBwb3NpdGlvbg0KZ2dwbG90KGhpdF85MF9jb3VudHMsIGFlcyh4ID0gYW5vbl9pZCwgeSA9IHRpbWVzX2hpdF85MCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRpbWVzX2hpdF85MCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMuNSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBvdmVyYWxsX2F2ZywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzAwMDAwMCIpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IG92ZXJhbGxfYXZnICsgMC41LCANCiAgICAgICAgICAgbGFiZWwgPSAiVGVhbSBBdmciLCBjb2xvciA9ICIjMDAwMDAwIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICBmYWNldF93cmFwKH4gUHJpbWFyeS5Qb3NpdGlvbiwgc2NhbGVzID0gImZyZWVfeCIpICsNCiAgbGFicyh0aXRsZSA9ICJDb3VudHMgb2Yg4omlOTAlIE1heCBWZWxvY2l0eSBieSBQbGF5ZXIgYW5kIFBvc2l0aW9uIiwNCiAgICAgICB5ID0gIlRpbWVzIOKJpTkwJSBNYXggVmVsb2NpdHkiLA0KICAgICAgIHggPSAiQXRobGV0ZSBJRCIpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpgYGB7cn0NCiMgUGxvdHMgZm9yIGVhY2ggcG9zaXRpb24NCg0KIyBHZXQgdGhlIG92ZXJhbGwgdGVhbSBhdmVyYWdlIG9uY2UgZnJvbSBoaXRfOTBfY291bnRzDQp0ZWFtX2F2ZyA8LSBtZWFuKGhpdF85MF9jb3VudHMkdGltZXNfaGl0XzkwKQ0KDQojIERlZmluZSAgcG9zaXRpb25zIHRvIGxvb3AgdGhyb3VnaA0KcG9zaXRpb25zIDwtIHVuaXF1ZShoaXRfOTBfY291bnRzJFByaW1hcnkuUG9zaXRpb24pDQoNCiMgUGxvdHRpbmcgZnVuY3Rpb246DQpwbG90X2hpdF85MF9ieV9wb3NpdGlvbiA8LSBmdW5jdGlvbihwb3MpIHsNCiAgcG9zaXRpb25fZGF0YSA8LSBoaXRfOTBfY291bnRzICU+JQ0KICAgIGZpbHRlcihQcmltYXJ5LlBvc2l0aW9uID09IHBvcykNCiAgDQogIHBvc19hdmcgPC0gbWVhbihwb3NpdGlvbl9kYXRhJHRpbWVzX2hpdF85MCkNCiAgDQogIGdncGxvdChwb3NpdGlvbl9kYXRhLCBhZXMoeCA9IGFub25faWQsIHkgPSB0aW1lc19oaXRfOTApKSArDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGltZXNfaGl0XzkwKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMy41KSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcG9zX2F2ZywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzU2NUE1QyIpICsNCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxLCB5ID0gcG9zX2F2ZyArIDAuNSwgDQogICAgICAgICAgICAgbGFiZWwgPSAiUG9zIEF2ZyIsIGNvbG9yID0gIiM1NjVBNUMiLCBzaXplID0gMywgaGp1c3QgPSAwKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IHRlYW1fYXZnICsgMC41LCANCiAgICAgICAgICAgICBsYWJlbCA9ICJUZWFtIEF2ZyIsIGNvbG9yID0gIiMwMDAwMDAiLCBzaXplID0gMywgaGp1c3QgPSAwKSArDQogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJUaW1lcyDiiaU5MCUgTWF4IFZlbG9jaXR5IOKAkyIsIHBvcyksDQogICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiUG9zaXRpb24gQXZnOiAiLCByb3VuZChwb3NfYXZnLCAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICIgfCBUZWFtIEF2ZzogIiwgcm91bmQodGVhbV9hdmcsIDEpKSwNCiAgICAgICAgIHkgPSAiQ291bnQiLA0KICAgICAgICAgeCA9ICJhbm9uX2lkIikgKw0KICAgIHRoZW1lX2NsYXNzaWMoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCn0NCg0KIyBMb29wIHRocm91Z2ggZWFjaCBwb3NpdGlvbiBhbmQgcHJpbnQgdGhlIHBsb3QNCnBsb3RzX2J5X3Bvc2l0aW9uIDwtIGxhcHBseShwb3NpdGlvbnMsIGZ1bmN0aW9uKHBvcykgew0KICBwcmludChwbG90X2hpdF85MF9ieV9wb3NpdGlvbihwb3MpKQ0KfSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBUYWJsZSBmb3IgYXZlcmFnZSB2YWx1ZXMgb2YgZWFjaCBwb3NpdGlvbg0KDQpwb3NpdGlvbl9hdmVyYWdlcyA8LSBoaXRfOTBfY291bnRzICU+JQ0KICBncm91cF9ieShQcmltYXJ5LlBvc2l0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z190aW1lc19oaXRfOTAgPSBtZWFuKHRpbWVzX2hpdF85MCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBhcnJhbmdlKGRlc2MoYXZnX3RpbWVzX2hpdF85MCkpDQoNCnBvc2l0aW9uX2F2ZXJhZ2VzX3dpdGhfdGVhbSA8LSBiaW5kX3Jvd3MoDQogIHRpYmJsZSgNCiAgICBQcmltYXJ5LlBvc2l0aW9uID0gIlRlYW0gQXZlcmFnZSIsDQogICAgYXZnX3RpbWVzX2hpdF85MCA9IHRlYW1fYXZnDQogICksDQogIHBvc2l0aW9uX2F2ZXJhZ2VzDQopDQoNCnBvc2l0aW9uX2F2ZXJhZ2VzX3dpdGhfdGVhbSAlPiUNCiAgZ3QoKSAlPiUNCiAgdGFiX2hlYWRlcigNCiAgICB0aXRsZSA9ICJBdmVyYWdlIFRpbWVzIOKJpTkwJSBNYXggVmVsb2NpdHkgYnkgUG9zaXRpb24gYW5kIFRlYW0iDQogICkNCmBgYA0KDQpEQnMgaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIHRpbWVzIHJlYWNoaW5nIOKJpTkwJSBvZiBNYXggdmVsb2NpdHkgKDE4LjQpLiBUaGUgT0wgaGFkIHRoZSBsb3dlc3QgKDYuOCkuIFRlYW0gYXZlcmFnZSB3YXMgMTEuOC4NCg0KYGBge3J9DQojIFBsb3QgY291bnRzIGZvciBlYWNoIHBvc2l0aW9uIGdyb3VwDQoNCiMgR2V0IHRoZSBvdmVyYWxsIHRlYW0gYXZlcmFnZSBvbmNlIGZyb20gaGl0XzkwX2NvdW50cw0KdGVhbV9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCg0KIyBEZWZpbmUgIHBvc2l0aW9ucyB0byBsb29wIHRocm91Z2gNCnBvc2l0aW9uc19ncm91cHMgPC0gdW5pcXVlKGhpdF85MF9jb3VudHMkUG9zaXRpb25fR3JvdXApDQoNCiMgUGxvdHRpbmcgZnVuY3Rpb246DQpwbG90X2hpdF85MF9ncm91cCA8LSBmdW5jdGlvbihwb3MpIHsNCiAgZ3JvdXBfZGF0YSA8LSBoaXRfOTBfY291bnRzICU+JQ0KICAgIGZpbHRlcihQb3NpdGlvbl9Hcm91cCA9PSBwb3MpDQogIA0KICBncm91cF9hdmcgPC0gbWVhbihncm91cF9kYXRhJHRpbWVzX2hpdF85MCkNCiAgDQogIGdncGxvdChncm91cF9kYXRhLCBhZXMoeCA9IGFub25faWQsIHkgPSB0aW1lc19oaXRfOTApKSArDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGltZXNfaGl0XzkwKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMy41KSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZ3JvdXBfYXZnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjNTY1QTVDIikgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBncm91cF9hdmcgKyAwLjUsIA0KICAgICAgICAgICAgIGxhYmVsID0gIkdyb3VwIEF2ZyIsIGNvbG9yID0gIiM1NjVBNUMiLCBzaXplID0gMywgaGp1c3QgPSAwKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IHRlYW1fYXZnICsgMC41LCANCiAgICAgICAgICAgICBsYWJlbCA9ICJUZWFtIEF2ZyIsIGNvbG9yID0gIiMwMDAwMDAiLCBzaXplID0gMywgaGp1c3QgPSAwKSArDQogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJUaW1lcyDiiaU5MCUgTWF4IFZlbG9jaXR5IOKAkyIsIHBvcyksDQogICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiR3JvdXAgQXZnOiAiLCByb3VuZChncm91cF9hdmcsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIiB8IFRlYW0gQXZnOiAiLCByb3VuZCh0ZWFtX2F2ZywgMSkpLA0KICAgICAgICAgeSA9ICJDb3VudCIsDQogICAgICAgICB4ID0gImFub25faWQiKSArDQogICAgdGhlbWVfY2xhc3NpYygpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KfQ0KDQojIExvb3AgdGhyb3VnaCBlYWNoIHBvc2l0aW9uIGFuZCBwcmludCB0aGUgcGxvdA0KcGxvdHNfYnlfZ3JvdXAgPC0gbGFwcGx5KHBvc2l0aW9uc19ncm91cHMsIGZ1bmN0aW9uKHBvcykgew0KICBwcmludChwbG90X2hpdF85MF9ncm91cChwb3MpKQ0KfSkNCg0KYGBgDQpUaGUgc2tpbGwgcG9zaXRpb24gZ3JvdXAgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgdGltZXMgcmVhY2hlZCDiiaU5MCUgTWF4IHdpdGggMTcuMSwgdGhlbiBjb21ibyB3aXRoIDkuMywgdGhlbiBiaWdzIHdpdGggOC45Lg0KDQpgYGB7cn0NCiMgTWFrZSBmYWNldCBwbG90IGZvciBwb3NpdGlvbiBncm91cHMgc28gdGhhdCBpdCBmaXRzIG9uIHRoZSBwcmVzZW50YXRpb24gc2xpZGVzIGJldHRlci4NCg0KIyBDYWxjdWxhdGUgYXZlcmFnZXMgZm9yIGVhY2ggZ3JvdXAgYW5kIHRoZSB0ZWFtDQpncm91cF9hdmdzIDwtIGhpdF85MF9jb3VudHMgJT4lDQogIGdyb3VwX2J5KFBvc2l0aW9uX0dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKGdyb3VwX2F2ZyA9IG1lYW4odGltZXNfaGl0XzkwKSkNCg0KdGVhbV9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCg0KIyBKb2luIGdyb3VwIGF2ZXJhZ2VzIGJhY2sgdG8gdGhlIG1haW4gZGF0YQ0KcGxvdF9kYXRhIDwtIGhpdF85MF9jb3VudHMgJT4lDQogIGxlZnRfam9pbihncm91cF9hdmdzLCBieSA9ICJQb3NpdGlvbl9Hcm91cCIpDQoNCiMgQ3JlYXRlIGxhYmVsIGRhdGEgd2l0aCBmb3JtYXR0ZWQgc3RyaW5ncw0KYXZnX2xhYmVscyA8LSBwbG90X2RhdGEgJT4lDQogIGdyb3VwX2J5KFBvc2l0aW9uX0dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGdyb3VwX2F2ZyA9IHVuaXF1ZShncm91cF9hdmcpLA0KICAgIHRlYW1fYXZnID0gdGVhbV9hdmcsDQogICAgbGFiZWxfeCA9IDEsDQogICAgZ3JvdXBfbGFiZWxfeSA9IGdyb3VwX2F2ZyArIDEsDQogICAgdGVhbV9sYWJlbF95ID0gdGVhbV9hdmcgKyAxLA0KICAgIGdyb3VwX2xhYmVsID0gcm91bmQoZ3JvdXBfYXZnLCAxKSwNCiAgICB0ZWFtX2xhYmVsID0gcm91bmQodGVhbV9hdmcsIDEpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKQ0KDQojIFBsb3QNCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gYW5vbl9pZCwgeSA9IHRpbWVzX2hpdF85MCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IGdyb3VwX2F2ZyksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fYXZnLCBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBjb2xvciA9ICIjNTY1QTVDIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IGF2Z19sYWJlbHMsIGFlcyh4ID0gbGFiZWxfeCwgeSA9IGdyb3VwX2xhYmVsX3ksIGxhYmVsID0gZ3JvdXBfbGFiZWwpLA0KICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwgaGp1c3QgPSAwLCBzaXplID0gMywgY29sb3IgPSAiIzAwMDAwMCIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBhdmdfbGFiZWxzLCBhZXMoeCA9IGxhYmVsX3gsIHkgPSB0ZWFtX2xhYmVsX3ksIGxhYmVsID0gdGVhbV9sYWJlbCksDQogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLCBoanVzdCA9IDAsIHNpemUgPSAzLCBjb2xvciA9ICIjNTY1QTVDIikgKw0KICBmYWNldF93cmFwKH4gUG9zaXRpb25fR3JvdXAsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIGxhYnModGl0bGUgPSAiVGltZXMgUmVhY2hlZCDiiaU5MCUgTWF4IFZlbG9jaXR5IGJ5IFBvc2l0aW9uIEdyb3VwIiwNCiAgICAgICB5ID0gIkNvdW50IiwgeCA9ICJhbm9uX2lkIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgcG9zaXRpb24gZ3JvdXAgYXZnIGNvdW50IHRhYmxlDQoNCiMgQ3JlYXRlIHRlYW0gYXZlcmFnZSByb3cNCnRlYW1fcm93IDwtIHRpYmJsZSgNCiAgR3JvdXAgPSAiVGVhbSBBdmciLA0KICBBdmVyYWdlID0gdGVhbV9hdmcNCikNCg0KIyBDcmVhdGUgZ3JvdXAgYXZlcmFnZSByb3dzDQpncm91cF9yb3dzIDwtIGhpdF85MF9jb3VudHMgJT4lDQogIGdyb3VwX2J5KFBvc2l0aW9uX0dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKEF2ZXJhZ2UgPSBtZWFuKHRpbWVzX2hpdF85MCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBhcnJhbmdlKGRlc2MoQXZlcmFnZSkpICU+JSAgIyBTb3J0IGZyb20gaGlnaGVzdCB0byBsb3dlc3QNCiAgcmVuYW1lKEdyb3VwID0gUG9zaXRpb25fR3JvdXApDQoNCiMgQ29tYmluZSB0aGVtICh0ZWFtIGZpcnN0KQ0KY29tYmluZWRfdGFibGUgPC0gYmluZF9yb3dzKHRlYW1fcm93LCBncm91cF9yb3dzKQ0KDQojIFZpZXcgdGhlIHRhYmxlDQpjb21iaW5lZF90YWJsZQ0KYGBgDQpObyBzdXJwcmlzZSB0aGF0IHRoZSBza2lsbCBwb3NpdGlvbiBncm91cCBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBjb3VudHMuIA0KDQpgYGB7cn0NCiMgPjkwJSBjb3VudHMgb3ZlciB0aGUgY291cnNlIG9mIHRoZSBzZWFzb24NCg0KIyBUcnlpbmcgdG8gZmluZCBvdXQgd2hlbiBhcmUgcGxheWVycyByZWFjaGluZyBhYm92ZSA5MCUgdGhlIG1vc3QNCg0KIyBDcmVhdGUgZGF0YXNldCB0aGF0IGhhcyB0aGUgY291bnQgb2YgPjkwJSBvZiBlYWNoIGRheQ0KZGFpbHlfOTBfY291bnRzIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgZmlsdGVyKEhpdC45MC5QZXJjZW50Lk1heCA9PSAiWWVzIikgJT4lDQogIGRpc3RpbmN0KGFub25faWQsIERhdGUpICU+JQ0KICBncm91cF9ieShEYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGRhaWx5X2hpdHMgPSBuKCkpDQoNCiMgUGxvdA0KZ2dwbG90KGRhaWx5XzkwX2NvdW50cywgYWVzKHggPSBEYXRlLCB5ID0gZGFpbHlfaGl0cykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gIiNDRkI4N0MiLCBsaW5ld2lkdGggPSAxKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gIiM1NjVBNUMiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiRGFpbHkgQ291bnQgb2YgUGxheWVycyBSZWFjaGluZyDiiaUgOTAlIG9mIE1heCBWZWxvY2l0eSIsDQogICAgICAgc3VidGl0bGUgPSAiT3ZlciB0aGUgMjAyNOKAkzI1IFRyYWluaW5nIFNlYXNvbiIsDQogICAgICAgeCA9ICJEYXRlIiwgeSA9ICJUaW1lcyByZWFjaGVkIOKJpSA5MCUiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQpEYXRhIGlzIG5vaXN5LCBzbyBsZXRzIHRyeSBncm91cGluZyBieSB3ZWVrIGluc3RlYWQgb2YgZXZlcnkgZGF5Lg0KDQpgYGB7cn0NCiMgPjkwJSBjb3VudHMgZm9yIGVhY2ggd2VlayBpbnN0ZWFkIG9mIGVhY2ggZGF5LiBXaWxsIGJlIGEgbGl0dGxlIGxlc3Mgbm9pc3kgdGhhbiB0aGUgZGFpbHkNCndlZWtseV85MF9jb3VudHMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JQ0KICBmaWx0ZXIoSGl0LjkwLlBlcmNlbnQuTWF4ID09ICJZZXMiKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgRGF0ZSkgJT4lDQogIG11dGF0ZSh3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSkgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBzdW1tYXJpc2Uod2Vla2x5X2hpdHMgPSBuKCkpDQoNCm1lYW5fdmFsIDwtIHJvdW5kKG1lYW4od2Vla2x5XzkwX2NvdW50cyR3ZWVrbHlfaGl0cyksIDEpDQoNCmdncGxvdCh3ZWVrbHlfOTBfY291bnRzLCBhZXMoeCA9IHdlZWssIHkgPSB3ZWVrbHlfaGl0cykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gIiNDRkI4N0MiLCBsaW5ld2lkdGggPSAxKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiIzAwMDAwMCIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiIzU2NUE1QyIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuNikgKw0KICBsYWJzKHRpdGxlID0gIldlZWtseSBDb3VudCBvZiBQbGF5ZXJzIFJlYWNoaW5nIOKJpSA5MCUgb2YgTWF4IFZlbG9jaXR5IiwNCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiV2VlayBBdmVyYWdlOiAiLCBtZWFuX3ZhbCksDQogICAgICAgeCA9ICJXZWVrIiwgeSA9ICJUaW1lcyByZWFjaGVkIOKJpSA5MCUiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCg0KYGBge3J9DQojIEJhciBjaGFydCBvZiB3ZWVrbHkg4omlOTAlIGNvdW50cw0KDQpnZ3Bsb3Qod2Vla2x5XzkwX2NvdW50cywgYWVzKHggPSB3ZWVrLCB5ID0gd2Vla2x5X2hpdHMpKSArDQogIGdlb21fY29sKGZpbGwgPSAiI0NGQjg3QyIsIGNvbG9yID0gIiM1NjVBNUMiLCB3aWR0aCA9IDUpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdlZWtseV9oaXRzKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjQsIA0KICAgICAgICAgICAgc2l6ZSA9IDIuNSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbl92YWwsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzU2NUE1QyIsIGxpbmV3aWR0aCA9IDAuNSkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIA0KICAgICAgICAgICB4ID0gbWluKHdlZWtseV85MF9jb3VudHMkd2VlaykgKyA3LCAgIyBBZGp1c3QgdGhpcyBkYXRlIHRvIHBsYWNlIHRoZSBsYWJlbA0KICAgICAgICAgICB5ID0gbWVhbl92YWwgKyAxLjUsICAgICAgICAgICAgICAgICAjIFNsaWdodGx5IGFib3ZlIHRoZSBsaW5lDQogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk1lYW46Iiwgcm91bmQobWVhbl92YWwsIDEpKSwNCiAgICAgICAgICAgc2l6ZSA9IDMuMjUsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogIGxhYnMoDQogICAgdGl0bGUgICAgPSAiV2Vla2x5IENvdW50IG9mIFBsYXllcnMgUmVhY2hpbmcg4omlIDkwJSBvZiBNYXggVmVsb2NpdHkiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIlNlYXNvbiBBdmVyYWdlIHBlciBXZWVrOiIsIHJvdW5kKG1lYW4od2Vla2x5XzkwX2NvdW50cyR3ZWVrbHlfaGl0cyksIDEpKSwNCiAgICB4ICAgICAgICA9ICJXZWVrIiwNCiAgICB5ICAgICAgICA9ICJUaW1lcyByZWFjaGVkIOKJpSA5MCUiDQogICkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrcyIsIGRhdGVfbGFiZWxzID0gIiViICVkIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpDQogICkNCmBgYA0KU29tZSBvZiBvdXIgbG93ZXN0IGF2ZXJhZ2UgY291bnRzIGZvciBhIHdlZWsgc2VlbSB0byBiZSBkdXJpbmcgdGhlIHNlYXNvbiwgd2hlcmVhcyBzb21lIG9mIG91ciBoaWdoZXN0IGNvdW50cyBzZWVtIHRvIGJlIHByZSBhbmQgcG9zdC1zZWFzb24uDQoNCiMjIFNob3VsZCB3ZSBjb25zaWRlciB0aGUgbnVtYmVyIG9mIHNwcmludGluZyBlZmZvcnRzIHRoYXQgYXRobGV0ZXMgYXJlIGNvbXBsZXRpbmc/DQoNCkhvdyBvZnRlbiBhdGhsZXRlcyBhcmUgaGl0dGluZyB3aGljaCB2ZWxvY2l0eSBiYW5kcyBpbiByZWxhdGlvbiB0byB0aGVpciB0b3Agc3BlZWRzLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIHN1bSBvZiB3ZWVrbHkgYmFuZCB0b3RhbHMNCg0KIyBNYWtlIHN1cmUgd2VlayBpcyBkZWZpbmVkDQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUgDQogIG11dGF0ZSh3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSkNCg0KIyBTdW0gZWZmb3J0cyBieSB3ZWVrIGFuZCBhdGhsZXRlDQp3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIFYyID0gc3VtKFZlbG9jaXR5LkJhbmQuMi5Ub3RhbC5FZmZvcnQuQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgVjMgPSBzdW0oVmVsb2NpdHkuQmFuZC4zLlRvdGFsLkVmZm9ydC5Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBWNCA9IHN1bShWZWxvY2l0eS5CYW5kLjQuVG90YWwuRWZmb3J0LkNvdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIFY1ID0gc3VtKFZlbG9jaXR5LkJhbmQuNS5Ub3RhbC5FZmZvcnQuQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgVjYgPSBzdW0oVmVsb2NpdHkuQmFuZC42LlRvdGFsLkVmZm9ydC5Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBWNyA9IHN1bShWZWxvY2l0eS5CYW5kLjcuVG90YWwuRWZmb3J0LkNvdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIFY4ID0gc3VtKFZlbG9jaXR5LkJhbmQuOC5Ub3RhbC5FZmZvcnQuQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgdG90YWxfZWZmb3J0cyA9IFYyICsgVjMgKyBWNCArIFY1ICsgVjYgKyBWNyArIFY4LA0KICAgIFdlZWtseV9NYXhfVmVsb2NpdHkgPSBtYXgoTWF4aW11bS5WZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBQbGF5ZXJfTWF4X1ZlbG9jaXR5ID0gZmlyc3QoUGxheWVyLk1heC5WZWxvY2l0eSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgcGN0X29mX21heF92ZWxvY2l0eSA9IChXZWVrbHlfTWF4X1ZlbG9jaXR5IC8gUGxheWVyX01heF9WZWxvY2l0eSkgKiAxMDANCiAgKQ0KDQojIENyZWF0ZSBwZXJjZW50YWdlIG9mIHdlZWtseSBlZmZvcnRzIGluIGVhY2ggYmFuZA0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIG11dGF0ZSgNCiAgICBwY3RfVjIgPSAoVjIgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjMgPSAoVjMgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjQgPSAoVjQgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjUgPSAoVjUgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjYgPSAoVjYgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjcgPSAoVjcgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMCwNCiAgICBwY3RfVjggPSAoVjggLyB0b3RhbF9lZmZvcnRzKSAqIDEwMA0KICApDQoNCndlZWtseV92ZWxvY2l0eV9lZmZvcnRzIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBmaWx0ZXIodG90YWxfZWZmb3J0cyA+IDApDQpgYGANCg0KDQpgYGB7cn0NCiMgUGxvdCBmb3IgZWFjaCBwbGF5ZXINCg0KIyBMb29wIG92ZXIgYWxsIHBsYXllcnMgYW5kIGRpc3BsYXkgdGhlaXIgcGxvdHMNCnVuaXF1ZSh3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyRhbm9uX2lkKSAlPiUNCiAgbGFwcGx5KGZ1bmN0aW9uKHBsYXllcl9pZCkgew0KICAgIA0KICAgICMgUHJlcGFyZSB0aGUgZGF0YSBmb3Igb25lIHBsYXllcg0KICAgIHBsYXllcl93ZWVrbHlfZGF0YSA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgICAgIGZpbHRlcihhbm9uX2lkID09IHBsYXllcl9pZCkgJT4lDQogICAgICBzZWxlY3Qod2VlaywgcGN0X1YyOnBjdF9WOCwgcGN0X29mX21heF92ZWxvY2l0eSkgJT4lDQogICAgICBwaXZvdF9sb25nZXIoDQogICAgICAgIGNvbHMgPSBzdGFydHNfd2l0aCgicGN0X1YiKSwNCiAgICAgICAgbmFtZXNfdG8gPSAidmVsb2NpdHlfYmFuZCIsDQogICAgICAgIHZhbHVlc190byA9ICJwZXJjZW50X2VmZm9ydCINCiAgICAgICkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIHZlbG9jaXR5X2JhbmQgPSBmYWN0b3IoDQogICAgICAgICAgdmVsb2NpdHlfYmFuZCwNCiAgICAgICAgICBsZXZlbHMgPSBwYXN0ZTAoInBjdF9WIiwgODoyKSwNCiAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoIlYiLCA4OjIpDQogICAgICAgICkNCiAgICAgICkNCiAgICANCiAgICAjIFNraXAgZW1wdHkgZGF0YQ0KICAgIGlmIChucm93KHBsYXllcl93ZWVrbHlfZGF0YSkgPT0gMCkgcmV0dXJuKE5VTEwpDQogICAgDQogICAgIyBHZW5lcmF0ZSBhbmQgcHJpbnQgdGhlIHBsb3QNCiAgICBwbG90IDwtIGdncGxvdChwbGF5ZXJfd2Vla2x5X2RhdGEsIGFlcyh4ID0gd2VlaykpICsNCiAgICAgIGdlb21fY29sKGFlcyh5ID0gcGVyY2VudF9lZmZvcnQsIGZpbGwgPSB2ZWxvY2l0eV9iYW5kKSwgcG9zaXRpb24gPSAic3RhY2siKSArDQogICAgICBnZW9tX2xpbmUoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBncm91cCA9IDEpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjIpICsNCiAgICAgIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgIlYyIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICJWMyIgPSAiZ3JlZW4yIiwNCiAgICAgICAgICAiVjQiID0gImdyZWVueWVsbG93IiwNCiAgICAgICAgICAiVjUiID0gInllbGxvdyIsDQogICAgICAgICAgIlY2IiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICJWNyIgPSAidG9tYXRvIiwNCiAgICAgICAgICAiVjgiID0gImZpcmVicmljayINCiAgKQ0KKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgICAgICBuYW1lID0gIkJhbmQgRGlzdHJpYnV0aW9uICglKSIsDQogICAgICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuLCBuYW1lID0gIldlZWtseSBNYXggVmVsb2NpdHkgKCUgb2YgUFIpIikNCiAgICAgICkgKw0KICAgICAgbGFicygNCiAgICAgICAgdGl0bGUgPSBwYXN0ZSgiVmVsb2NpdHkgQmFuZCBFZmZvcnQgJSBhbmQgU3BlZWQgVHJlbmQgZm9yIFBsYXllciIsIHBsYXllcl9pZCksDQogICAgICAgIHggPSAiV2VlayIsDQogICAgICAgIGZpbGwgPSAiVmVsb2NpdHkgQmFuZCINCiAgICAgICkgKw0KICAgICAgdGhlbWVfY2xhc3NpYygpDQogICAgDQogICAgcHJpbnQocGxvdCkgICMgRGlzcGxheSBwbG90DQogICAgDQogICAgcmV0dXJuKE5VTEwpDQogIH0pDQpgYGANCg0KRWFjaCBwbGF5ZXIgaXMgZGlmZmVyZW50IGluIHJlYWNoaW5nIGRpZmZlcmVudCB2ZWxvY2l0eSBiYW5kcyBpbiByZWxhdGlvbiB0byB0aGVpciB0b3Agc3BlZWRzLiBTb21lIHBsYXllcnMsIElEXzExIGZvciBleGFtcGxlLCByYXJlbHkgZW50ZXIgYmFuZHMgNiBvciBoaWdoZXIsIGV2ZW4gaWYgdGhleSBhcmUgY2xvc2UgdG8gMTAwJSBvZiB0aGVpciBtYXggdmVsb2NpdHkuIFRoaXMgc2hvd3MgdGhlIGlzc3VlcyB3aXRoIHVzaW5nIGFic29sdXRlIGJhbmRzIGZvciB0aGUgd2hvbGUgdGVhbS4NCg0KDQpgYGB7cn0NCiMgRmlsdGVyIGZvciBJRF8xMSBhbmQgcmVzaGFwZSB0aGUgZGF0YQ0KcGxheWVyX3dlZWtseV9kYXRhIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCA9PSAiSURfMTEiKSAlPiUNCiAgc2VsZWN0KHdlZWssIHBjdF9WMjpwY3RfVjgsIHBjdF9vZl9tYXhfdmVsb2NpdHkpICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IHN0YXJ0c193aXRoKCJwY3RfViIpLA0KICAgIG5hbWVzX3RvID0gInZlbG9jaXR5X2JhbmQiLA0KICAgIHZhbHVlc190byA9ICJwZXJjZW50X2VmZm9ydCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZlbG9jaXR5X2JhbmQgPSBmYWN0b3IoDQogICAgICB2ZWxvY2l0eV9iYW5kLA0KICAgICAgbGV2ZWxzID0gcGFzdGUwKCJwY3RfViIsIDg6MiksDQogICAgICBsYWJlbHMgPSBwYXN0ZTAoIlYiLCA4OjIpDQogICAgKQ0KICApDQoNCiMgUGxvdCBmb3IgSURfMTENCmdncGxvdChwbGF5ZXJfd2Vla2x5X2RhdGEsIGFlcyh4ID0gd2VlaykpICsNCiAgZ2VvbV9jb2woYWVzKHkgPSBwZXJjZW50X2VmZm9ydCwgZmlsbCA9IHZlbG9jaXR5X2JhbmQpLCBwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKA0KICAgICAgIlYyIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgIlYzIiA9ICJncmVlbjIiLA0KICAgICAgIlY0IiA9ICJncmVlbnllbGxvdyIsDQogICAgICAiVjUiID0gInllbGxvdyIsDQogICAgICAiVjYiID0gIm9yYW5nZSIsDQogICAgICAiVjciID0gInRvbWF0byIsDQogICAgICAiVjgiID0gImZpcmVicmljayINCiAgICApDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbmFtZSA9ICJCYW5kIERpc3RyaWJ1dGlvbiAoJSkiLA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuLCBuYW1lID0gIldlZWtseSBNYXggVmVsb2NpdHkgKCUgb2YgUFIpIikNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVmVsb2NpdHkgQmFuZCBFZmZvcnQgJSBhbmQgU3BlZWQgVHJlbmQgZm9yIFBsYXllciBJRF8xMSIsDQogICAgeCA9ICJXZWVrIiwNCiAgICBmaWxsID0gIlZlbG9jaXR5IEJhbmQiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBQaWNrIG9uZSBwbGF5ZXINCnBsYXllcl9pZCA8LSAiSURfOTMiDQoNCiMgRmlsdGVyIGRhdGEgZm9yIHRoYXQgcGxheWVyIGFuZCB3ZWVrcw0KcGxheWVyX2RhdGEgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGZpbHRlcihhbm9uX2lkID09IHBsYXllcl9pZCkNCg0KIyBQbG90IG9mIFRvdGFsIFNwcmludGluZyBFZmZvcnRzIHBlciBXZWVrDQpnZ3Bsb3QocGxheWVyX2RhdGEsIGFlcyh4ID0gd2VlaywgeSA9IHRvdGFsX2VmZm9ydHMpKSArDQogIGdlb21fbGluZShjb2xvciA9ICIjQ0ZCODdDIiwgc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjQ0ZCODdDIiwgc2l6ZSA9IDIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9IHBhc3RlKCJUb3RhbCBTcHJpbnRpbmcgRWZmb3J0cyBwZXIgV2VlayBmb3IgUGxheWVyIiwgcGxheWVyX2lkKSwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiVG90YWwgU3ByaW50aW5nIEVmZm9ydHMiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KI1Bsb3Qgb2YgU3ByaW50IEVmZm9ydCBEaXN0cmlidXRpb24gYnkgVmVsb2NpdHkgQmFuZA0KDQojIFJlc2hhcGUgYWJzb2x1dGUgY291bnRzIHRvIGxvbmcgZm9ybWF0DQpwbGF5ZXJfY291bnRzX2xvbmcgPC0gcGxheWVyX2RhdGEgJT4lDQogIHNlbGVjdCh3ZWVrLCBWMiwgVjMsIFY0LCBWNSwgVjYsIFY3LCBWOCkgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gVjI6VjgsDQogICAgbmFtZXNfdG8gPSAidmVsb2NpdHlfYmFuZCIsDQogICAgdmFsdWVzX3RvID0gImVmZm9ydF9jb3VudCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZlbG9jaXR5X2JhbmQgPSBmYWN0b3IodmVsb2NpdHlfYmFuZCwgbGV2ZWxzID0gcGFzdGUwKCJWIiwgODoyKSkNCiAgKQ0KDQpnZ3Bsb3QocGxheWVyX2NvdW50c19sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBlZmZvcnRfY291bnQsIGZpbGwgPSB2ZWxvY2l0eV9iYW5kKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gcGFzdGUoIlNwcmludCBFZmZvcnQgRGlzdHJpYnV0aW9uIGJ5IFZlbG9jaXR5IEJhbmQgZm9yIFBsYXllciIsIHBsYXllcl9pZCksDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIlNwcmludCBFZmZvcnQgQ291bnQiLA0KICAgIGZpbGwgPSAiVmVsb2NpdHkgQmFuZCINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQojIyMjIEV4cGxvcmluZyBjb3JyZWxhdGlvbnMgYmV0d2VlbiBiYW5kcyBhbmQgbWF4IHNwZWVkcw0KDQpgYGB7cn0NCg0KDQojIFNlbGVjdCByZWxldmFudCBjb2x1bW5zDQpjb3JfZGF0YSA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgc2VsZWN0KHBjdF9vZl9tYXhfdmVsb2NpdHksIHBjdF9WMiwgcGN0X1YzLCBwY3RfVjQsIHBjdF9WNSwgcGN0X1Y2LCBwY3RfVjcsIHBjdF9WOCkNCg0KIyBDb21wdXRlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yX21hdHJpeCA8LSBjb3IoY29yX2RhdGEsIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KDQpjb3JfbWF0cml4DQoNCg0KIyBDb21wdXRlIGNvcnJlbGF0aW9ucyB3aXRoIHBjdF9vZl9tYXhfdmVsb2NpdHkNCmNvcl92YWx1ZXMgPC0gY29yX2RhdGEgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoLXBjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgICAgICAgICAgICAgICAgfiBjb3IoLngsIGNvcl9kYXRhJHBjdF9vZl9tYXhfdmVsb2NpdHksIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSkpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAiY29ycmVsYXRpb24iKQ0KDQojIFBsb3Qgd2l0aCBsYWJlbHMNCmdncGxvdChjb3JfdmFsdWVzLCBhZXMoeCA9IHJlb3JkZXIodmFyaWFibGUsIGNvcnJlbGF0aW9uKSwgeSA9IGNvcnJlbGF0aW9uKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiNDRkI4N0MiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChjb3JyZWxhdGlvbiwgMikpLCANCiAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKGNvcl92YWx1ZXMkY29ycmVsYXRpb24gPj0gMCwgLTAuMSwgMS4xKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkNvcnJlbGF0aW9uIEJldHdlZW4gUGVyY2VudCBpbiBCYW5kIGFuZCBQZXJjZW50IG9mIE1heCBWZWxvY2l0eSIsDQogICAgeCA9ICJCYW5kIiwNCiAgICB5ID0gIlBlYXJzb24gQ29ycmVsYXRpb24iDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkgDQpgYGANClY0IGFuZCBWNSBhcHBlYXIgdG8gYmUgc2xpZ2h0bHkgbW9yZSBwcmVkaWN0aXZlIG9mIHRvcC1zcGVlZCBhY2hpZXZlbWVudCB0aGFuIFY2LCBWNyBvciBWOCDigJQgcG9zc2libHkgYmVjYXVzZSB0aGV5J3JlIG1vcmUgZnJlcXVlbnRseSByZWFjaGVkIHpvbmVzLg0KDQpWMiBoYXMgYSBtb2RlcmF0ZWx5IHN0cm9uZyBuZWdhdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwIHdpdGggcmVhY2hpbmcgdG9wLXNwZWVkLiBXaGVuIGF0aGxldGVzIHNwZW5kIG1vcmUgZWZmb3J0IGluIHRoaXMgbG93LXNwZWVkIGJhbmQsIHRoZWlyIHdlZWtseSB0b3Agc3BlZWQgKGFzICUgb2YgbWF4KSB0ZW5kcyB0byBiZSBsb3dlci4NCg0KYGBge3J9DQojIExpbmVhciBtb2RlbCB3aXRoIGFsbCBiYW5kcyBwcmVkaWN0aW5nIHBjdF9vZl9tYXhfdmVsb2NpdHkNCm1vZGVsX2FsbF9iYW5kcyA8LSBsbSgNCiAgcGN0X29mX21heF92ZWxvY2l0eSB+IHBjdF9WMiArIHBjdF9WMyArIHBjdF9WNCArIHBjdF9WNSArIHBjdF9WNiArIHBjdF9WNyArIHBjdF9WOCwNCiAgZGF0YSA9IHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzDQopDQpzdW1tYXJ5KG1vZGVsX2FsbF9iYW5kcykNCmBgYA0KYGBge3J9DQojIENvbXB1dGUgY2hhbmdlIGluIHRvcCBzcGVlZHMgd2Vlay10by13ZWVrDQp3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgYXJyYW5nZShhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZSgNCiAgICBwY3Rfb2ZfbWF4X3ZlbG9jaXR5X2NoYW5nZSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkgLSBsYWcocGN0X29mX21heF92ZWxvY2l0eSkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQojIFNjYXR0ZXJwbG90IHdpdGggc21vb3RoaW5nIGxpbmUgZm9yIFY4IGJhbmQgZWZmb3J0DQoNCiMgQ3JlYXRlIFY4IGRhdGEgc2V0IHRoYXQgb25seSBpbmNsdWRlcyB3aGVuIHNvbWVvbmUgd2FzIGluIFY4IGZvciBhIGdpdmVuIHdlZWsNClY4IDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBmaWx0ZXIocGN0X1Y4ID4gMCkNCg0KIyBQbG90DQpnZ3Bsb3QoVjgsIGFlcyh4ID0gcGN0X1Y4LCB5ID0gcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IFRSVUUsIGNvbG9yID0gImRhcmtyZWQiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkgdnMuIFY4IEJhbmQgVXNhZ2UiLA0KICAgIHggPSAiUGVyY2VudCBvZiBUb3RhbCBFZmZvcnQgaW4gVjggQmFuZCAoJSkiLA0KICAgIHkgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KDQojIFNjYXR0ZXJwbG90IGZvciBWMiBiYW5kIGVmZm9ydCB2cy4gUGVyY2VudCBvZiBNYXggVmVsb2NpdHkNCiMgQ3JlYXRlIFYyIGRhdGEgc2V0IHRoYXQgb25seSBpbmNsdWRlcyB3aGVuIHNvbWVvbmUgd2FzIGluIFYyIGZvciBhIGdpdmVuIHdlZWsNClYyIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBmaWx0ZXIocGN0X1YyID4gMCkNCg0KIyBQbG90DQpnZ3Bsb3QoVjIsIGFlcyh4ID0gcGN0X1YyLCB5ID0gcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IFRSVUUsIGNvbG9yID0gImRhcmtyZWQiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkgdnMuIFYyIEJhbmQgVXNhZ2UiLA0KICAgIHggPSAiUGVyY2VudCBvZiBUb3RhbCBFZmZvcnQgaW4gVjIgQmFuZCAoJSkiLA0KICAgIHkgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpgYGB7cn0NCndlZWtseV92ZWxvY2l0eV9lZmZvcnRzIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKA0KICAgIGxhZ19WMiA9IGxhZyhWMiksDQogICAgbGFnX1YzID0gbGFnKFYzKSwNCiAgICBsYWdfVjQgPSBsYWcoVjQpLA0KICAgIGxhZ19WNSA9IGxhZyhWNSksDQogICAgbGFnX1Y2ID0gbGFnKFY2KSwNCiAgICBsYWdfVjcgPSBsYWcoVjcpLA0KICAgIGxhZ19WOCA9IGxhZyhWOCksDQogICAgDQogICAgbGFnX3BjdF9WMiA9IGxhZyhwY3RfVjIpLA0KICAgIGxhZ19wY3RfVjMgPSBsYWcocGN0X1YzKSwNCiAgICBsYWdfcGN0X1Y0ID0gbGFnKHBjdF9WNCksDQogICAgbGFnX3BjdF9WNSA9IGxhZyhwY3RfVjUpLA0KICAgIGxhZ19wY3RfVjYgPSBsYWcocGN0X1Y2KSwNCiAgICBsYWdfcGN0X1Y3ID0gbGFnKHBjdF9WNyksDQogICAgbGFnX3BjdF9WOCA9IGxhZyhwY3RfVjgpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNYXliZSBncm91cCBieSBwb3NpdGlvbg0KDQojIEdldCBQbGF5ZXIgcG9zaXRpb25zDQpwbGF5ZXJfcG9zaXRpb25zIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIHNlbGVjdChhbm9uX2lkLCBQcmltYXJ5LlBvc2l0aW9uKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojIEpvaW4gUG9zaXRpb24gaW50byB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cw0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGxlZnRfam9pbihwbGF5ZXJfcG9zaXRpb25zLCBieSA9ICJhbm9uX2lkIikNCg0KIyBQb3NpdGlvbiBncm91cHMNCndlZWtseV92ZWxvY2l0eV9lZmZvcnRzIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBtdXRhdGUoDQogICAgUG9zaXRpb25fR3JvdXAgPSBjYXNlX3doZW4oDQogICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgYygiUUIiLCAiTEIiLCAiVEUiLCAiUkIiKSB+ICJDT01CTyIsDQogICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgYygiT0wiLCAiREwiLCAiREUiKSB+ICJCSUdTIiwNCiAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBjKCJXUiIsICJEQiIsICJEQiwgV1IiKSB+ICJTS0lMTCIsDQogICAgICBUUlVFIH4gIk9USEVSIg0KICAgICkNCiAgKSAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uX0dyb3VwICE9ICJPVEhFUiIpDQoNCmdncGxvdCh3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cywgYWVzKHggPSBQb3NpdGlvbl9Hcm91cCwgeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHlfY2hhbmdlKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJza3libHVlIiwgb3V0bGllci5jb2xvciA9ICJyZWQiLCBvdXRsaWVyLnNpemUgPSAxLjUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJXZWVrbHkgQ2hhbmdlIGluICUgb2YgTWF4IFZlbG9jaXR5IGJ5IFBvc2l0aW9uIiwNCiAgICB4ID0gIlBvc2l0aW9uIiwNCiAgICB5ID0gIiUgQ2hhbmdlIGluIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQp3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGdyb3VwX2J5KHdlZWssIFBvc2l0aW9uX0dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG1lYW5fVjIgPSBtZWFuKFYyLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjMgPSBtZWFuKFYzLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjQgPSBtZWFuKFY0LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjUgPSBtZWFuKFY1LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjYgPSBtZWFuKFY2LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjcgPSBtZWFuKFY3LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fVjggPSBtZWFuKFY4LCBuYS5ybSA9IFRSVUUpLA0KICAgIA0KICAgIG1lYW5fbGFnX1YyID0gbWVhbihsYWdfVjIsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9sYWdfVjMgPSBtZWFuKGxhZ19WMywgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX2xhZ19WNCA9IG1lYW4obGFnX1Y0LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fbGFnX1Y1ID0gbWVhbihsYWdfVjUsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9sYWdfVjYgPSBtZWFuKGxhZ19WNiwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX2xhZ19WNyA9IG1lYW4obGFnX1Y3LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fbGFnX1Y4ID0gbWVhbihsYWdfVjgsIG5hLnJtID0gVFJVRSksDQogICAgDQogICAgbWVhbl9wY3RfVjIgPSBtZWFuKHBjdF9WMiwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3BjdF9WMyA9IG1lYW4ocGN0X1YzLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fcGN0X1Y0ID0gbWVhbihwY3RfVjQsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfVjUgPSBtZWFuKHBjdF9WNSwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3BjdF9WNiA9IG1lYW4ocGN0X1Y2LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fcGN0X1Y3ID0gbWVhbihwY3RfVjcsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfVjggPSBtZWFuKHBjdF9WOCwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkgPSBtZWFuKFdlZWtseV9NYXhfVmVsb2NpdHksIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfbWF4X3ZlbG9jaXR5ID0gbWVhbihwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fcGN0X21heF92ZWxvY2l0eV9jaGFuZ2UgPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHlfY2hhbmdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIG5fcGxheWVycyA9IG4oKQ0KICApICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQoNCmBgYHtyfQ0KDQojIFBpdm90IGxvbmdlciB0byBnZXQgYmFuZHMgaW4gb25lIGNvbHVtbiBmb3IgZWFzaWVyIHBsb3R0aW5nDQpiYW5kX2xvbmcgPC0gd2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IHN0YXJ0c193aXRoKCJtZWFuX3BjdF9WIiksDQogICAgbmFtZXNfdG8gPSAidmVsb2NpdHlfYmFuZCIsDQogICAgdmFsdWVzX3RvID0gIm1lYW5fcGN0X2VmZm9ydCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZlbG9jaXR5X2JhbmQgPSBmYWN0b3IodmVsb2NpdHlfYmFuZCwgbGV2ZWxzID0gcGFzdGUwKCJtZWFuX3BjdF9WIiwgMjo4KSkNCiAgKQ0KDQpnZ3Bsb3QoYmFuZF9sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BjdF9lZmZvcnQsIGNvbG9yID0gdmVsb2NpdHlfYmFuZCkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArDQogIGZhY2V0X3dyYXAofiBQb3NpdGlvbl9Hcm91cCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIldlZWtseSBNZWFuICUgRWZmb3J0IGluIFZlbG9jaXR5IEJhbmRzIGJ5IFBvc2l0aW9uIEdyb3VwIiwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiTWVhbiAlIEVmZm9ydCIsDQogICAgY29sb3IgPSAiVmVsb2NpdHkgQmFuZCINCiAgKSArDQogICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgIm1lYW5fcGN0X1YyIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICJtZWFuX3BjdF9WMyIgPSAiZ3JlZW4yIiwNCiAgICAgICAgICAibWVhbl9wY3RfVjQiID0gImdyZWVueWVsbG93IiwNCiAgICAgICAgICAibWVhbl9wY3RfVjUiID0gInllbGxvdyIsDQogICAgICAgICAgIm1lYW5fcGN0X1Y2IiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICJtZWFuX3BjdF9WNyIgPSAidG9tYXRvIiwNCiAgICAgICAgICAibWVhbl9wY3RfVjgiID0gImZpcmVicmljayINCiAgKQ0KKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KVGhpcyBzaG93cyB0aGUgbWVhbiBwZXJjZW50IG9mIHRvdGFsIGVmZm9ydHMgaW4gZWFjaCBiYW5kIG92ZXIgd2Vla3MgYnkgZWFjaCBwb3NpdGlvbiBncm91cC4NCg0KYGBge3J9DQojIFBsb3QgbWVhbiB3ZWVrbHkgbWF4IHZlbG9jaXR5DQpnZ3Bsb3Qod2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHksIGNvbG9yID0gUG9zaXRpb25fR3JvdXApKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTWVhbiBXZWVrbHkgTWF4IFZlbG9jaXR5IGJ5IFBvc2l0aW9uIEdyb3VwIiwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiTWVhbiBXZWVrbHkgTWF4IFZlbG9jaXR5Ig0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCg0KIyBQbG90IG1lYW4gJSBtYXggdmVsb2NpdHkgY2hhbmdlDQpnZ3Bsb3Qod2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BjdF9tYXhfdmVsb2NpdHlfY2hhbmdlLCBjb2xvciA9IFBvc2l0aW9uX0dyb3VwKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1lYW4gJSBDaGFuZ2UgaW4gTWF4IFZlbG9jaXR5IGJ5IFBvc2l0aW9uIEdyb3VwIiwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiTWVhbiAlIENoYW5nZSBpbiBNYXggVmVsb2NpdHkiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgIyBQbG90IHRoZSBzdGFja2VkIGxpbmVzIGZvciBiYW5kIGVmZm9ydCAlDQogIGdlb21fbGluZShkYXRhID0gYmFuZF9sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BjdF9lZmZvcnQsIGNvbG9yID0gdmVsb2NpdHlfYmFuZCksIHNpemUgPSAxKSArDQogIGdlb21fbGluZShkYXRhID0gd2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwLA0KICAgICAgICAgICAgYWVzKHggPSB3ZWVrLCB5ID0gbWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5KSwNCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuMikgKw0KDQogIGZhY2V0X3dyYXAofiBQb3NpdGlvbl9Hcm91cCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIldlZWtseSBNZWFuICUgRWZmb3J0IGluIFZlbG9jaXR5IEJhbmRzIGJ5IFBvc2l0aW9uIEdyb3VwXG53aXRoIE1lYW4gV2Vla2x5IE1heCBWZWxvY2l0eSIsDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIk1lYW4gJSBFZmZvcnQiLA0KICAgIGNvbG9yID0gIlZlbG9jaXR5IEJhbmQiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgIm1lYW5fcGN0X1YyIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICJtZWFuX3BjdF9WMyIgPSAiZ3JlZW4yIiwNCiAgICAgICAgICAibWVhbl9wY3RfVjQiID0gImdyZWVueWVsbG93IiwNCiAgICAgICAgICAibWVhbl9wY3RfVjUiID0gInllbGxvdyIsDQogICAgICAgICAgIm1lYW5fcGN0X1Y2IiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICJtZWFuX3BjdF9WNyIgPSAidG9tYXRvIiwNCiAgICAgICAgICAibWVhbl9wY3RfVjgiID0gImZpcmVicmljayINCiAgKQ0KKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQojIERlZmluZSBtYXggZWZmb3J0IGFuZCBtYXggdmVsb2NpdHkgcmFuZ2UNCm1heF9lZmZvcnQgPC0gNjcNCm1heF92ZWxvY2l0eSA8LSBtYXgod2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwJG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhID0gYmFuZF9sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BjdF9lZmZvcnQsIGNvbG9yID0gdmVsb2NpdHlfYmFuZCksIHNpemUgPSAxKSArDQogIA0KICAjIFNjYWxlIG1heF92ZWxvY2l0eSB0byAlIGVmZm9ydCBzY2FsZSB0byBvdmVybGF5IG9uIGxlZnQgYXhpcw0KICBnZW9tX2xpbmUoZGF0YSA9IHdlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCwgDQogICAgICAgICAgICBhZXMoeCA9IHdlZWssIHkgPSAobWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5IC8gbWF4X3ZlbG9jaXR5KSAqIG1heF9lZmZvcnQsIGNvbG9yID0gIk1heCBWZWxvY2l0eSIpLA0KICAgICAgICAgICAgc2l6ZSA9IDEuMSkgKw0KICANCiAgZmFjZXRfd3JhcCh+IFBvc2l0aW9uX0dyb3VwKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBuYW1lID0gIk1lYW4gJSBFZmZvcnQiLA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuICogbWF4X3ZlbG9jaXR5IC8gbWF4X2VmZm9ydCwgbmFtZSA9ICJNZWFuIFdlZWtseSBNYXggVmVsb2NpdHkgKG0vcykiKQ0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAibWVhbl9wY3RfVjIiID0gImRhcmtncmVlbiIsDQogICAgICAibWVhbl9wY3RfVjMiID0gImdyZWVuMiIsDQogICAgICAibWVhbl9wY3RfVjQiID0gImdyZWVueWVsbG93IiwNCiAgICAgICJtZWFuX3BjdF9WNSIgPSAieWVsbG93IiwNCiAgICAgICJtZWFuX3BjdF9WNiIgPSAib3JhbmdlIiwNCiAgICAgICJtZWFuX3BjdF9WNyIgPSAidG9tYXRvIiwNCiAgICAgICJtZWFuX3BjdF9WOCIgPSAiZmlyZWJyaWNrIiwNCiAgICAgICJNYXggVmVsb2NpdHkiID0gImJsYWNrIg0KICAgICkNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiV2Vla2x5IE1lYW4gJSBFZmZvcnQgaW4gVmVsb2NpdHkgQmFuZHMgYnkgUG9zaXRpb24gR3JvdXBcbndpdGggTWVhbiBXZWVrbHkgTWF4IFZlbG9jaXR5IiwNCiAgICB4ID0gIldlZWsiLA0KICAgIGNvbG9yID0gIlZlbG9jaXR5IEJhbmQiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgTW9kZWxpbmcNCg0KIyBTZXBhcmF0ZSBieSBwb3NpdGlvbiBncm91cA0KYmlncyA8LSB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAgJT4lDQogIGZpbHRlcihQb3NpdGlvbl9Hcm91cCA9PSAiQklHUyIpDQoNCnNraWxsIDwtIHdlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uX0dyb3VwID09ICJTS0lMTCIpDQoNCmNvbWJvIDwtIHdlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uX0dyb3VwID09ICJDT01CTyIpDQoNCiMgQmlncyBNb2RlbA0KbW9kZWxfYmlncyA8LSBsbSgNCiAgbWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5IH4gbWVhbl9sYWdfVjIgKyBtZWFuX2xhZ19WMyArIG1lYW5fbGFnX1Y0ICsgbWVhbl9sYWdfVjUgKyBtZWFuX2xhZ19WNiArIG1lYW5fbGFnX1Y3ICsgbWVhbl9sYWdfVjgsIGRhdGEgPSBiaWdzDQogICkNCnN1bW1hcnkobW9kZWxfYmlncykNCg0KIyBTa2lsbCBNb2RlbA0KbW9kZWxfc2tpbGwgPC0gbG0oDQogIG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSB+IG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMgKyBtZWFuX2xhZ19WNCArIG1lYW5fbGFnX1Y1ICsgbWVhbl9sYWdfVjYgKyBtZWFuX2xhZ19WNyArIG1lYW5fbGFnX1Y4LCBkYXRhID0gc2tpbGwNCiAgKQ0Kc3VtbWFyeShtb2RlbF9za2lsbCkNCg0KIyBDb21ibyBNb2RlbA0KbW9kZWxfY29tYm8gPC0gbG0oDQogIG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSB+IG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMgKyBtZWFuX2xhZ19WNCArIG1lYW5fbGFnX1Y1ICsgbWVhbl9sYWdfVjYgKyBtZWFuX2xhZ19WNyArIG1lYW5fbGFnX1Y4LCBkYXRhID0gY29tYm8pDQpzdW1tYXJ5KG1vZGVsX2NvbWJvKQ0KYGBgDQoNCmBgYHtyfQ0KIyBHcm91cCBjb3JyZWxhdGlvbjoNCg0KIyBTZWxlY3QgcmVsZXZhbnQgdmFyaWFibGVzDQp2YXJzIDwtIGMoIm1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSIsICJtZWFuX2xhZ19WMiIsICJtZWFuX2xhZ19WMyIsIA0KICAgICAgICAgICJtZWFuX2xhZ19WNCIsICJtZWFuX2xhZ19WNSIsICJtZWFuX2xhZ19WNiIsICJtZWFuX2xhZ19WNyIsICJtZWFuX2xhZ19WOCIpDQoNCiMgQ29ycmVsYXRpb24gbWF0cmljZXMNCmNvcl9iaWdzIDwtIGNvcihiaWdzWywgdmFyc10sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KY29yX3NraWxsIDwtIGNvcihza2lsbFssIHZhcnNdLCB1c2UgPSAiY29tcGxldGUub2JzIikNCmNvcl9jb21ibyA8LSBjb3IoY29tYm9bLCB2YXJzXSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQpyb3VuZChjb3JfYmlncywgMikNCnJvdW5kKGNvcl9za2lsbCwgMikNCnJvdW5kKGNvcl9jb21ibywgMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBSZWR1Y2UgZm9yIGJldHRlciBtb2RlbGluZw0KYmlncyA8LSBiaWdzICU+JQ0KICBtdXRhdGUoDQogICAgbG93X2JhbmQgPSAobWVhbl9sYWdfVjIgKyBtZWFuX2xhZ19WMykgLyAyLA0KICAgIG1pZF9iYW5kID0gKG1lYW5fbGFnX1Y0ICsgbWVhbl9sYWdfVjUgKyBtZWFuX2xhZ19WNikgLyAzLA0KICAgIGhpZ2hfYmFuZCA9IChtZWFuX2xhZ19WNyArIG1lYW5fbGFnX1Y4KSAvIDINCiAgKQ0KbW9kZWxfYmlnc19zaW1wbGUgPC0gbG0obWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5IH4gbG93X2JhbmQgKyBtaWRfYmFuZCArIGhpZ2hfYmFuZCwgZGF0YSA9IGJpZ3MpDQpzdW1tYXJ5KG1vZGVsX2JpZ3Nfc2ltcGxlKQ0KDQoNCiMgU2tpbGwgR3JvdXANCnNraWxsIDwtIHNraWxsICU+JQ0KICBtdXRhdGUoDQogICAgbG93X2JhbmQgPSAobWVhbl9sYWdfVjIgKyBtZWFuX2xhZ19WMyArIG1lYW5fbGFnX1Y0KSAvIDMsDQogICAgbWlkX2JhbmQgPSAobWVhbl9sYWdfVjUgKyBtZWFuX2xhZ19WNikgLyAyLA0KICAgIGhpZ2hfYmFuZCA9IChtZWFuX2xhZ19WNyArIG1lYW5fbGFnX1Y4KSAvIDINCiAgKQ0KbW9kZWxfc2tpbGxfc2ltcGxlIDwtIGxtKG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSB+IGxvd19iYW5kICsgbWlkX2JhbmQgKyBoaWdoX2JhbmQsIGRhdGEgPSBza2lsbCkNCnN1bW1hcnkobW9kZWxfc2tpbGxfc2ltcGxlKQ0KDQojIENvbWJvIGdyb3VwDQpjb21ibyA8LSBjb21ibyAlPiUNCiAgbXV0YXRlKA0KICAgIGxvd19iYW5kID0gKG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMpIC8gMiwNCiAgICBtaWRfYmFuZCA9IChtZWFuX2xhZ19WNCArIG1lYW5fbGFnX1Y1KSAvIDIsDQogICAgaGlnaF9iYW5kID0gKG1lYW5fbGFnX1Y2ICsgbWVhbl9sYWdfVjcgKyBtZWFuX2xhZ19WOCkgLyAzDQogICkNCm1vZGVsX2NvbWJvX3NpbXBsZSA8LSBsbShtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkgfiBsb3dfYmFuZCArIG1pZF9iYW5kICsgaGlnaF9iYW5kLCBkYXRhID0gY29tYm8pDQpzdW1tYXJ5KG1vZGVsX2NvbWJvX3NpbXBsZSkNCmBgYA0KQmlncyBtb2RlbDoNCiAgU3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvdmVyYWxsIGFuZCBleHBsYWlucyBhcm91bmQgMzMlIG9mIHRoZSB2YXJpYXRpb24gb2YgbWF4IHZlbG9jaXR5LiBUaGUgbG93IGJhbmQgaXMgc2lnbmlmaWNhbnRseSBuZWdhdGl2ZS4gRXZlcnkgdW5pdCBpbmNyZWFzZSBpbiB0aGUgbG93IGJhbmQgcmVzdWx0cyBpbiBhIGRlY3JlYXNlIGluIG1heCB2ZWxvY2l0eS4gVGhlIG1pZCBhbmQgaGlnaCBiYW5kcyBhcmUgbm90IHNpZ25pZmljYW50IGJ1dCB0aGUgaGlnaCBiYW5kIHNob3dzIGEgcG9zaXRpdmUgdHJlbmQuDQogIEJhc2VkIG9uIHRoaXMgbW9kZWwsIGZvciBiaWdzLCBtb3JlIGxvdy1iYW5kIGVmZm9ydCBtYXkgcmVkdWNlIHdlZWtseSB0b3Agc3BlZWQsIHBvc3NpYmx5IGluZGljYXRpbmcgdW5kZXJleHBvc3VyZSBvZiBoaWdoIHNwZWVkcy4gSGlnaC1iYW5kIGVmZm9ydHMgbWlnaHQgaGVscCwgYnV0IGFyZW7igJl0IGNsZWFybHkgaW1wYWN0ZnVsIGluIHRoaXMgZ3JvdXAuDQoNClNraWxsIG1vZGVsOg0KICBBbG1vc3Qgc2lnbmlmaWNhbnQuIExvdyBiYW5kIGhhcyBhIG5lZ2F0aXZlIGVmZmVjdCwgbWlkIGJhbmQgaGFzIGEgcG9zaXRpdmUgZWZmZWN0LCBhbmQgaGlnaCBiYW5kIGlzIG5vdCBzaWduaWZpY2FudC4gDQogIEJhc2VkIG9uIHRoaXMgbW9kZWwsIGZvciBza2lsbCBwbGF5ZXJzLCBtb3JlIGxvdy1pbnRlbnNpdHkgZXhwb3N1cmUgc2VlbXMgZGV0cmltZW50YWwgdG8gbWF4IHNwZWVkLCB3aGlsZSBtb2RlcmF0ZSBiYW5kIChWNeKAk1Y2KSBleHBvc3VyZSBtYXkgZW5oYW5jZSBpdC4NCiAgDQpDb21ibyBtb2RlbDoNCiAgTW9kZWwgaXMgbm90IHNpZ25pZmljYW50LiBUaGUgY29tYm8gcG9zaXRpb24gZ3JvdXAgY29udGFpbnMgbWFueSBkaWZmZXJlbnQgdHlwZSBvZiBwb3NpdGlvbnMgKFFCcywgUkJzLCBURXMsIExCcykgd2l0aCBkaWZmZXJlbnQgbW92ZW1lbnQgYW5kIHZlbG9jaXRpZXMuIFRoaXMgdmFyaWFiaWxpdHkgbWF5IG9ic2N1cmUgcmVsYXRpb25zaGlwcy4NCiAgDQpBY3Jvc3MgZ3JvdXBzLCBsb3ctYmFuZCBleHBvc3VyZSBpcyBjb25zaXN0ZW50bHkgbmVnYXRpdmVseSByZWxhdGVkIHRvIHBlYWsgd2Vla2x5IHNwZWVkIOKAlCBzdWdnZXN0aW5nIHRoYXQgdG9vIG11Y2ggbG93LWludGVuc2l0eSB3b3JrIG1heSByZWR1Y2UgdGhlIGNhcGFjaXR5IHRvIHJlYWNoIGhpZ2ggc3BlZWRzLg0KDQojIyBBcmUgcmVsYXRpdmUgZWZmb3J0cyBhbmQgYmFuZHMgbW9yZSBhZHZhbnRhZ2VvdXMgdGhhbiB0aGUgYWJzb2x1dGUgYmFuZHMgcHJvdmlkZWQ/DQoNCkxvb2sgYXQgSURfMTEncyBwbG90IGZyb20gdGhlIGxhc3QgcXVlc3Rpb24uIFRoaXMgcGxheWVyIGlzIGFuIG9mZmVuc2l2ZSBsaW5lbWFuLiBMb29raW5nIGF0IHRoZSBwbG90LCBoZSBpcyBtb3N0bHkgaW4gYmFuZCAyIGFuZCAzIHdoaWxlIG5ldmVyIHJlYWNoaW5nIGJhbmRzIDcgb3IgOCBhbmQgcmFyZWx5IHJlYWNoaW5nIGJhbmQgNi4gRGVzcGl0ZSB0aGlzLCB0aGlzIGF0aGxldGUncyB3ZWVrbHkgbWF4IHZlbG9jaXR5IGlzIGFsd2F5cyBhdCBsZWFzdCA3NSUgb2YgdGhlaXIgYWxsLXRpbWUgbWF4IHZlbG9jaXR5LiBPbmx5IGxvb2tpbmcgYXQgdGhlIGFic29sdXRlIGJhbmRzIHRoYXQgYXJlIHByb3ZpZGVkLCB3ZSBtaWdodCBjb21lIHRvIHRoZSBjb25jbHVzaW9uIHRoYXQgSURfMTEgaXMgbm90IGFjaGlldmluZyBoaWdoIHJ1bm5pbmcgc3BlZWRzLCBob3dldmVyIGFmdGVyIGxvb2tpbmcgYXQgaGlzIG1heCB2ZWxvY2l0eSBlZmZvcnRzIHJlbGF0aXZlIHRvIGhpcyBhbGwtdGltZSBtYXggdmVsb2NpdHksIGhlIGlzIHJlYWNoaW5nIGhpZ2ggcnVubmluZyBzcGVlZHMuDQoNCiMjIyMgQ3JlYXRlIFJlbGF0aXZlIEJhbmRzIGZvciBJRF8xMQ0KDQpgYGB7cn0NCiMgQ3JlYXRlIFJlbGF0aXZlIEJhbmRzIGZvciBJRF8xMQ0KDQojIFByZXBhcmUgSURfMTEgZGF0YXNldCB3aXRoIHJlbGF0aXZlIGJhbmRzDQpJRF8xMSA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lIA0KICBmaWx0ZXIoYW5vbl9pZCA9PSAiSURfMTEiLCBNYXhpbXVtLlZlbG9jaXR5ICE9IDApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKE1heGltdW0uVmVsb2NpdHkpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpLA0KICAgIHBjdF9vZl9tYXggPSAoTWF4aW11bS5WZWxvY2l0eSAvIFBsYXllci5NYXguVmVsb2NpdHkpICogMTAwLA0KICAgICMgQ3JlYXRlIFJlbGF0aXZlIEJhbmRzDQogICAgcmVsYXRpdmVfYmFuZCA9IGN1dCgNCiAgICAgIHBjdF9vZl9tYXgsDQogICAgICBicmVha3MgPSBjKDIwLCA0MCwgNTAsIDYwLCA3MCwgODAsIDkwLCAxMDApLA0KICAgICAgbGFiZWxzID0gYygiVjIgKDIwLTQwJSkiLCAiVjMgKDQwLTUwJSkiLCJWNCAoNTAtNjAlKSIgLCJWNSAoNjAtNzAlKSIsICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSwNCiAgICAgIHJpZ2h0ID0gVFJVRSwgIyBTZXQgdG8gVFJVRSB0byBpbmNsdWRlIDEwMCUgaW4gYmFuZA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFICAjIEluY2x1ZGVzIDAgaW4gZmlyc3QgYmFuZA0KICAgICksDQogICAgIyBSZXZlcnNlIGZhY3RvciBsZXZlbHMgc28gaGlnaGVzdCBiYW5kIGlzIG9uIHRvcCBpbiBwbG90DQogIHJlbGF0aXZlX2JhbmQgPSBmYWN0b3IoDQogICAgcmVsYXRpdmVfYmFuZCwNCiAgICBsZXZlbHMgPSByZXYoYygiVjIgKDIwLTQwJSkiLCAiVjMgKDQwLTUwJSkiLCJWNCAoNTAtNjAlKSIgLCJWNSAoNjAtNzAlKSIsICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSkNCiAgKQ0KICApDQoNCiMgU3VtbWFyaXplIGNvdW50cyBwZXIgd2VlayBhbmQgcmVsYXRpdmUgYmFuZA0Kd2Vla2x5X2VmZm9ydCA8LSBJRF8xMSAlPiUNCiAgZmlsdGVyKCFpcy5uYShyZWxhdGl2ZV9iYW5kKSkgJT4lDQogIGdyb3VwX2J5KHdlZWssIHJlbGF0aXZlX2JhbmQpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgbXV0YXRlKHBjdF9lZmZvcnQgPSAoY291bnQgLyBzdW0oY291bnQpKSAqIDEwMCkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIFBpdm90IHdpZGVyIHRvIGdldCBwZXJjZW50YWdlcyBwZXIgYmFuZCBhcyBjb2x1bW5zDQp3ZWVrbHlfZWZmb3J0X3dpZGUgPC0gd2Vla2x5X2VmZm9ydCAlPiUNCiAgc2VsZWN0KHdlZWssIHJlbGF0aXZlX2JhbmQsIHBjdF9lZmZvcnQpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVsYXRpdmVfYmFuZCwgdmFsdWVzX2Zyb20gPSBwY3RfZWZmb3J0LCB2YWx1ZXNfZmlsbCA9IDApDQoNCiMgQ2FsY3VsYXRlIHdlZWtseSBtYXggdmVsb2NpdHkgJSBvZiBwbGF5ZXIgbWF4IHZlbG9jaXR5DQp3ZWVrbHlfbWF4X3ZlbG9jaXR5IDwtIElEXzExICU+JQ0KICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgc3VtbWFyaXNlKHBjdF9vZl9tYXhfdmVsb2NpdHkgPSBtYXgocGN0X29mX21heCksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgSm9pbiB0aGUgbWF4IHZlbG9jaXR5ICUgYmFjayB0byB3aWRlIGVmZm9ydCBkYXRhDQp3ZWVrbHlfZWZmb3J0X3dpZGUgPC0gd2Vla2x5X2VmZm9ydF93aWRlICU+JQ0KICBsZWZ0X2pvaW4od2Vla2x5X21heF92ZWxvY2l0eSwgYnkgPSAid2VlayIpDQoNCiMgUGl2b3QgbG9uZ2VyIGZvciBzdGFja2VkIGJhciBwbG90dGluZw0Kd2Vla2x5X2VmZm9ydF9sb25nIDwtIHdlZWtseV9lZmZvcnRfd2lkZSAlPiUNCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBjKCJWMiAoMjAtNDAlKSIsICJWMyAoNDAtNTAlKSIsIlY0ICg1MC02MCUpIiAsIlY1ICg2MC03MCUpIiwgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpLA0KICAgIG5hbWVzX3RvID0gInJlbGF0aXZlX2JhbmQiLA0KICAgIHZhbHVlc190byA9ICJwY3RfZWZmb3J0Ig0KICApICU+JQ0KICBtdXRhdGUoDQogICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcigNCiAgICAgIHJlbGF0aXZlX2JhbmQsDQogICAgICBsZXZlbHMgPSByZXYoYygiVjIgKDIwLTQwJSkiLCAiVjMgKDQwLTUwJSkiLCJWNCAoNTAtNjAlKSIgLCJWNSAoNjAtNzAlKSIsICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSkNCiAgICApDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgUGxvdCBSZWxhdGl2ZSBCYW5kcyBmb3IgSURfMTENCmdncGxvdCh3ZWVrbHlfZWZmb3J0X2xvbmcsIGFlcyh4ID0gd2VlaywgeSA9IHBjdF9lZmZvcnQsIGZpbGwgPSByZWxhdGl2ZV9iYW5kKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAiVjIgKDIwLTQwJSkiID0gImRhcmtncmVlbiIsDQogICAgICAiVjMgKDQwLTUwJSkiID0gImdyZWVuMiIsDQogICAgICAiVjQgKDUwLTYwJSkiID0gImdyZWVueWVsbG93IiwNCiAgICAgICJWNSAoNjAtNzAlKSIgPSAieWVsbG93IiwNCiAgICAgICJWNiAoNzAtODAlKSIgPSAib3JhbmdlIiwNCiAgICAgICJWNyAoODAtOTAlKSIgPSAidG9tYXRvIiwNCiAgICAgICJWOCAoOTAtMTAwJSkiID0gImRhcmtyZWQiDQogICAgKQ0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJSZWxhdGl2ZSBWZWxvY2l0eSBCYW5kIEVmZm9ydCAlIHBlciBXZWVrLCBJRF8xMSIsDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIlBlcmNlbnQgb2YgV2Vla2x5IEVmZm9ydHMiLA0KICAgIGZpbGwgPSAiUmVsYXRpdmUgVmVsb2NpdHkgQmFuZCINCiAgKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiKQ0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCkNvbXBhcmluZyB0aGlzIHdpdGggdGhlIGFic29sdXRlIGJhbmRzIGdyYXBoIG9mIElEXzExLCB3ZSBjYW4gc2VlIHRoYXQgdGhpcyBpcyBhIG11Y2ggYmV0dGVyIHJlcHJlc2VudGF0aW9uIG9mIHRoZWlyIHJ1bm5pbmcgc3BlZWRzLg0KDQojIyMjIFJlbGF0aXZlIEJhbmRzIGZvciBlYWNoIGF0aGxldGUNCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGNyZWF0ZSByZWxhdGl2ZSBiYW5kcyBhbmQgcGxvdCB0aGVzZSBmb3IgZWFjaCBhdGhsZXRlDQoNCiMgQXJyYW5nZSBkYXRhIHNvIHRoYXQgdGhlIHBsb3Qgd2lsbCBkaXNwbGF5IGluIGFub25faWQgb3JkZXINCmNsZWFuX2Fub25zIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGFycmFuZ2UoYW5vbl9pZCkNCg0KIyBMb29wIG92ZXIgYWxsIGF0aGxldGVzIGFuZCBjcmVhdGUgdGhlaXIgcmVsYXRpdmUgYmFuZHMgcGxvdHMNCnVuaXF1ZShjbGVhbl9hbm9ucyRhbm9uX2lkKSAlPiUNCiAgbGFwcGx5KGZ1bmN0aW9uKHBsYXllcl9pZCkgew0KICAgICMgUHJlcGFyZSBkYXRhIGZvciBwbGF5ZXINCiAgICBwbGF5ZXJfZGF0YSA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICAgICAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JQ0KICAgICAgZmlsdGVyKGFub25faWQgPT0gcGxheWVyX2lkLCBNYXhpbXVtLlZlbG9jaXR5ICE9IDAsICFpcy5uYShNYXhpbXVtLlZlbG9jaXR5KSkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpLA0KICAgICAgICBwY3Rfb2ZfbWF4ID0gKE1heGltdW0uVmVsb2NpdHkgLyBQbGF5ZXIuTWF4LlZlbG9jaXR5KSAqIDEwMCwNCiAgICAgICAgIyBDcmVhdGUgUmVsYXRpdmUgQmFuZHMNCiAgICAgICAgcmVsYXRpdmVfYmFuZCA9IGN1dCgNCiAgICAgICAgICBwY3Rfb2ZfbWF4LA0KICAgICAgICAgIGJyZWFrcyA9IGMoMjAsIDQwLCA1MCwgNjAsIDcwLCA4MCwgOTAsIDEwMCksDQogICAgICAgICAgbGFiZWxzID0gYygiVjIgKDIwLTQwJSkiLCAiVjMgKDQwLTUwJSkiLCAiVjQgKDUwLTYwJSkiLCAiVjUgKDYwLTcwJSkiLA0KICAgICAgICAgICAgICAgICAgICAgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpLA0KICAgICAgICAgIHJpZ2h0ID0gVFJVRSwgIyBTZXQgdG8gdHJ1ZSB0byBpbmNsdWRlIDEwMCUNCiAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUgICMgU2V0IHRvIHRydWUgdG8gaW5jbHVkZSAwIGluIDFzdCBpbnRlcnZhbA0KICAgICAgICApLA0KICAgICAgICAjIFJldmVyc2UgZmFjdG9yIGxldmVscyBzbyBoaWdoZXN0IGJhbmQgaXMgb24gdG9wIGluIHBsb3QNCiAgICAgICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcigNCiAgICAgICAgICByZWxhdGl2ZV9iYW5kLA0KICAgICAgICAgIGxldmVscyA9IHJldihjKCJWMiAoMjAtNDAlKSIsICJWMyAoNDAtNTAlKSIsICJWNCAoNTAtNjAlKSIsICJWNSAoNjAtNzAlKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpKQ0KICAgICAgICApDQogICAgICApDQogICAgDQogICAgaWYgKG5yb3cocGxheWVyX2RhdGEpID09IDApIHJldHVybihOVUxMKSAgIyBza2lwIGVtcHR5DQoNCiAgICAjIFN1bW1hcml6ZSBjb3VudHMgcGVyIHdlZWsgYW5kIHJlbGF0aXZlIGJhbmQNCiAgICB3ZWVrbHlfZWZmb3J0IDwtIHBsYXllcl9kYXRhICU+JQ0KICAgICAgZmlsdGVyKCFpcy5uYShyZWxhdGl2ZV9iYW5kKSkgJT4lDQogICAgICBncm91cF9ieSh3ZWVrLCByZWxhdGl2ZV9iYW5kKSAlPiUNCiAgICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogICAgICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgICAgIG11dGF0ZShwY3RfZWZmb3J0ID0gKGNvdW50IC8gc3VtKGNvdW50KSkgKiAxMDApICU+JQ0KICAgICAgdW5ncm91cCgpDQoNCiAgICAjIFBpdm90IHdpZGVyIHRvIGdldCBwZXJjZW50YWdlcyBwZXIgYmFuZCBhcyBjb2x1bW5zDQogICAgd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnQgJT4lDQogICAgICBzZWxlY3Qod2VlaywgcmVsYXRpdmVfYmFuZCwgcGN0X2VmZm9ydCkgJT4lDQogICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVsYXRpdmVfYmFuZCwgdmFsdWVzX2Zyb20gPSBwY3RfZWZmb3J0LCB2YWx1ZXNfZmlsbCA9IDApDQoNCiAgICAjIENhbGN1bGF0ZSB3ZWVrbHkgbWF4IHZlbG9jaXR5ICUgb2YgcGxheWVyIG1heCB2ZWxvY2l0eQ0KICAgIHdlZWtseV9tYXhfdmVsb2NpdHkgPC0gcGxheWVyX2RhdGEgJT4lDQogICAgICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgICAgIHN1bW1hcmlzZShwY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gbWF4KHBjdF9vZl9tYXgpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQogICAgIyBKb2luDQogICAgd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnRfd2lkZSAlPiUNCiAgICAgIGxlZnRfam9pbih3ZWVrbHlfbWF4X3ZlbG9jaXR5LCBieSA9ICJ3ZWVrIikNCiAgICANCiAgICAjIFBpdm90IGxvbmdlciBmb3Igc3RhY2tlZCBiYXIgcGxvdHRpbmcNCiAgICB3ZWVrbHlfZWZmb3J0X2xvbmcgPC0gd2Vla2x5X2VmZm9ydF93aWRlICU+JQ0KICAgICAgcGl2b3RfbG9uZ2VyKA0KICAgICAgICAgIGNvbHMgPSAtYyh3ZWVrLCBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwNCiAgICAgICAgbmFtZXNfdG8gPSAicmVsYXRpdmVfYmFuZCIsDQogICAgICAgIHZhbHVlc190byA9ICJwY3RfZWZmb3J0Ig0KICAgICAgKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcigNCiAgICAgICAgICByZWxhdGl2ZV9iYW5kLA0KICAgICAgICAgIGxldmVscyA9IHJldihjKCJWMiAoMjAtNDAlKSIsICJWMyAoNDAtNTAlKSIsICJWNCAoNTAtNjAlKSIsICJWNSAoNjAtNzAlKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpKQ0KICAgICAgICApDQogICAgICApDQogICAgDQogICAgIyBQbG90DQogICAgcGxvdCA8LSBnZ3Bsb3Qod2Vla2x5X2VmZm9ydF9sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBwY3RfZWZmb3J0LCBmaWxsID0gcmVsYXRpdmVfYmFuZCkpICsNCiAgICAgIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIikgKw0KICAgICAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4yKSArDQogICAgICBnZW9tX3BvaW50KGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIpICsNCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgICAgICB2YWx1ZXMgPSBjKA0KICAgICAgICAgICJWMiAoMjAtNDAlKSIgPSAiZGFya2dyZWVuIiwNCiAgICAgICAgICAiVjMgKDQwLTUwJSkiID0gImdyZWVuMiIsDQogICAgICAgICAgIlY0ICg1MC02MCUpIiA9ICJncmVlbnllbGxvdyIsDQogICAgICAgICAgIlY1ICg2MC03MCUpIiA9ICJ5ZWxsb3ciLA0KICAgICAgICAgICJWNiAoNzAtODAlKSIgPSAib3JhbmdlIiwNCiAgICAgICAgICAiVjcgKDgwLTkwJSkiID0gInRvbWF0byIsDQogICAgICAgICAgIlY4ICg5MC0xMDAlKSIgPSAiZGFya3JlZCINCiAgICAgICAgKQ0KICAgICAgKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgICAgIG5hbWUgPSAiUGVyY2VudCBvZiBXZWVrbHkgRWZmb3J0IiwNCiAgICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiKQ0KICAgICAgKSArDQogICAgICBsYWJzKA0KICAgICAgICB0aXRsZSA9IHBhc3RlKCJSZWxhdGl2ZSBWZWxvY2l0eSBCYW5kIEVmZm9ydCAlIGFuZCBNYXggVmVsb2NpdHkgVHJlbmQgZm9yIFBsYXllciIsIHBsYXllcl9pZCksDQogICAgICAgIHggPSAiV2VlayIsDQogICAgICAgIGZpbGwgPSAiUmVsYXRpdmUgVmVsb2NpdHkgQmFuZCINCiAgICAgICkgKw0KICAgICAgdGhlbWVfY2xhc3NpYygpDQoNCiAgICBwcmludChwbG90KQ0KICAgIA0KICAgIHJldHVybihOVUxMKQ0KICB9KQ0KYGBgDQoNCg0KYGBge3J9DQojIENyZWF0ZSByZWxhdGl2ZSBiYW5kcyBmb3IgYWxsIGF0aGxldGVzDQoNCiMgRGVmaW5lIHRoZSB2ZWxvY2l0eSBiYW5kIGxhYmVscw0KYmFuZF9sZXZlbHMgPC0gYygiVjIgKDIwLTQwJSkiLCAiVjMgKDQwLTUwJSkiLCAiVjQgKDUwLTYwJSkiLCAiVjUgKDYwLTcwJSkiLA0KICAgICAgICAgICAgICAgICAgICAgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpDQoNCiMgQ3JlYXRlIGV4cG9zdXJlIGRhdGEgZnJhbWUgZm9yIGFsbCBwbGF5ZXJzDQp3ZWVrbHlfc3ByaW50X2V4cG9zdXJlIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgZmlsdGVyKE1heGltdW0uVmVsb2NpdHkgIT0gMCwgIWlzLm5hKE1heGltdW0uVmVsb2NpdHkpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpLA0KICAgIHBjdF9vZl9tYXggPSAoTWF4aW11bS5WZWxvY2l0eSAvIFBsYXllci5NYXguVmVsb2NpdHkpICogMTAwLA0KICAgIHJlbGF0aXZlX2JhbmQgPSBjdXQoDQogICAgICBwY3Rfb2ZfbWF4LA0KICAgICAgYnJlYWtzID0gYygyMCwgNDAsIDUwLCA2MCwgNzAsIDgwLCA5MCwgMTAwLjEpLA0KICAgICAgbGFiZWxzID0gYmFuZF9sZXZlbHMsDQogICAgICByaWdodCA9IFRSVUUsICAjIHdpbGwgaW5jbHVkZSAxMDANCiAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRQ0KICAgICksDQogICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcihyZWxhdGl2ZV9iYW5kLCBsZXZlbHMgPSBiYW5kX2xldmVscykNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShyZWxhdGl2ZV9iYW5kKSkgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIHdlZWssIHJlbGF0aXZlX2JhbmQpICU+JQ0KICBzdW1tYXJpc2UoZWZmb3J0X2NvdW50ID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgd2VlaykgJT4lDQogIG11dGF0ZSgNCiAgICB0b3RhbF9lZmZvcnRzID0gc3VtKGVmZm9ydF9jb3VudCksDQogICAgcGN0X2VmZm9ydCA9IChlZmZvcnRfY291bnQgLyB0b3RhbF9lZmZvcnRzKSAqIDEwMA0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHNlbGVjdChhbm9uX2lkLCB3ZWVrLCByZWxhdGl2ZV9iYW5kLCBwY3RfZWZmb3J0KSAlPiUNCiAgcGl2b3Rfd2lkZXIoDQogICAgbmFtZXNfZnJvbSA9IHJlbGF0aXZlX2JhbmQsDQogICAgdmFsdWVzX2Zyb20gPSBwY3RfZWZmb3J0LA0KICAgIHZhbHVlc19maWxsID0gMA0KICApDQoNCiMgQWRkIHdlZWtseSBtYXggdmVsb2NpdHkgJSBvZiBtYXgNCndlZWtseV9tYXhfcGN0IDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgZmlsdGVyKE1heGltdW0uVmVsb2NpdHkgIT0gMCwgIWlzLm5hKE1heGltdW0uVmVsb2NpdHkpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpLA0KICAgIHBjdF9vZl9tYXggPSAoTWF4aW11bS5WZWxvY2l0eSAvIFBsYXllci5NYXguVmVsb2NpdHkpICogMTAwDQogICkgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIHdlZWspICU+JQ0KICBzdW1tYXJpc2UocGN0X29mX21heF92ZWxvY2l0eSA9IG1heChwY3Rfb2ZfbWF4LCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIEZpbmFsIGRhdGEgc2V0DQpyZWxhdGl2ZV9iYW5kcyA8LSB3ZWVrbHlfc3ByaW50X2V4cG9zdXJlICU+JQ0KICBsZWZ0X2pvaW4od2Vla2x5X21heF9wY3QsIGJ5ID0gYygiYW5vbl9pZCIsICJ3ZWVrIikpDQoNCiMgQ29udmVydCB0byBsb25nIGZvciBwbG90dGluZw0KcmVsYXRpdmVfYmFuZHNfbG9uZyA8LSByZWxhdGl2ZV9iYW5kcyAlPiUNCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBzdGFydHNfd2l0aCgiViIpLA0KICAgIG5hbWVzX3RvID0gInJlbGF0aXZlX2JhbmQiLA0KICAgIHZhbHVlc190byA9ICJwY3RfZWZmb3J0Ig0KICApICU+JQ0KICBtdXRhdGUoDQogICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcihyZWxhdGl2ZV9iYW5kLCBsZXZlbHMgPSBiYW5kX2xldmVscykNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KQ09NQk8gPC0gYygiUUIiLCJMQiIsIlRFIiwiUkIiLCAiSUxCIikNCkJJRyA8LSBjKCJPTCIsICJETCIsICJERSIsICJEVCIpDQpTS0lMTCA8LSBjKCJXUiIsICJEQiIsICJDQiIsICJTQUYiKQ0KUG9zaXRpb25zIDwtIGMoIkNPTUJPIiwgIkJJRyIsICJTS0lMTCIpDQoNCndlZWtseV9zcHJpbnRfZXhwb3N1cmUgPC0gbGVmdF9qb2luKHdlZWtseV9zcHJpbnRfZXhwb3N1cmUsIHBsYXllcl9wb3NpdGlvbnMsIGJ5ID0gImFub25faWQiLCByZWxhdGlvbnNoaXAgPSAibWFueS10by1tYW55IikNCg0Kd2Vla2x5X3NwcmludF9leHBvc3VyZSA8LSB3ZWVrbHlfc3ByaW50X2V4cG9zdXJlICU+JQ0KICBtdXRhdGUoUG9zaXRpb24gPSBjYXNlX3doZW4oUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIENPTUJPIH4gIkNPTUJPIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgQklHIH4gIkJJRyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIFNLSUxMIH4gIlNLSUxMIikpDQoNCndlZWtseV9zcHJpbnRfZXhwb3N1cmUgPC0gZGlzdGluY3Qod2Vla2x5X3NwcmludF9leHBvc3VyZSkNCg0KbWF4X3ZlbG9jaXRpZXMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHNbLGMoImFub25faWQiLCJ3ZWVrIiwicGN0X29mX21heF92ZWxvY2l0eSIpXQ0KDQp3ZWVrbHlfc3ByaW50X2V4cG9zdXJlIDwtIGxlZnRfam9pbih3ZWVrbHlfc3ByaW50X2V4cG9zdXJlLCBtYXhfdmVsb2NpdGllcywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSwgcmVsYXRpb25zaGlwPSJtYW55LXRvLW1hbnkiKQ0KDQoNCg0KDQpzcHJpbnRfZXhwb3N1cmVfYmlnIDwtIHdlZWtseV9zcHJpbnRfZXhwb3N1cmUgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQklHIikgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBtdXRhdGUoYXZnX3BjdF9tYXggPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHkpLA0KICAgICAgICAgYXZnX1Y4ID0gbWVhbihgVjggKDkwLTEwMCUpYCksDQogICAgICAgICBhdmdfVjcgPSBtZWFuKGBWNyAoODAtOTAlKWApLA0KICAgICAgICAgYXZnX1Y2ID0gbWVhbihgVjYgKDcwLTgwJSlgKSwNCiAgICAgICAgIGF2Z19WNSA9IG1lYW4oYFY1ICg2MC03MCUpYCksDQogICAgICAgICBhdmdfVjQgPSBtZWFuKGBWNCAoNTAtNjAlKWApLA0KICAgICAgICAgYXZnX1YzID0gbWVhbihgVjMgKDQwLTUwJSlgKSwNCiAgICAgICAgIGF2Z19WMiA9IG1lYW4oYFYyICgyMC00MCUpYCkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG5hLm9taXQoKQ0KDQpzcHJpbnRfZXhwb3N1cmVfY29tYm8gPC0gd2Vla2x5X3NwcmludF9leHBvc3VyZSAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uID09ICJDT01CTyIpICAlPiUNCiAgZ3JvdXBfYnkod2VlaykgJT4lDQogIG11dGF0ZShhdmdfcGN0X21heCA9IG1lYW4ocGN0X29mX21heF92ZWxvY2l0eSksDQogICAgICAgICBhdmdfVjggPSBtZWFuKGBWOCAoOTAtMTAwJSlgKSwNCiAgICAgICAgIGF2Z19WNyA9IG1lYW4oYFY3ICg4MC05MCUpYCksDQogICAgICAgICBhdmdfVjYgPSBtZWFuKGBWNiAoNzAtODAlKWApLA0KICAgICAgICAgYXZnX1Y1ID0gbWVhbihgVjUgKDYwLTcwJSlgKSwNCiAgICAgICAgIGF2Z19WNCA9IG1lYW4oYFY0ICg1MC02MCUpYCksDQogICAgICAgICBhdmdfVjMgPSBtZWFuKGBWMyAoNDAtNTAlKWApLA0KICAgICAgICAgYXZnX1YyID0gbWVhbihgVjIgKDIwLTQwJSlgKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgbmEub21pdCgpDQoNCnNwcmludF9leHBvc3VyZV9za2lsbCA8LSB3ZWVrbHlfc3ByaW50X2V4cG9zdXJlICU+JQ0KICBmaWx0ZXIoUG9zaXRpb24gPT0gIlNLSUxMIikgICU+JQ0KICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgbXV0YXRlKGF2Z19wY3RfbWF4ID0gbWVhbihwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwNCiAgICAgICAgIGF2Z19WOCA9IG1lYW4oYFY4ICg5MC0xMDAlKWApLA0KICAgICAgICAgYXZnX1Y3ID0gbWVhbihgVjcgKDgwLTkwJSlgKSwNCiAgICAgICAgIGF2Z19WNiA9IG1lYW4oYFY2ICg3MC04MCUpYCksDQogICAgICAgICBhdmdfVjUgPSBtZWFuKGBWNSAoNjAtNzAlKWApLA0KICAgICAgICAgYXZnX1Y0ID0gbWVhbihgVjQgKDUwLTYwJSlgKSwNCiAgICAgICAgIGF2Z19WMyA9IG1lYW4oYFYzICg0MC01MCUpYCksDQogICAgICAgICBhdmdfVjIgPSBtZWFuKGBWMiAoMjAtNDAlKWApKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBuYS5vbWl0KCkNCiAgDQpgYGANCg0KV2hlbiBJIGJ1aWx0IHRoZSBtb2RlbHMgdGhhdCBjb25zaWRlcmVkIHRoZSByZWxhdGl2ZSBiYW5kcyBpbnN0ZWFkIG9mIHRoZSBhYnNvbHV0ZSBiYW5kcyBwcm92aWRlZCwgSSBmb3VuZCB0aGF0IGluaXRpYWxseSwgdGhlIHJlc3VsdHMgd2VyZSBhIGxvdCBiZXR0ZXIgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWxzIHdpdGggdGhlIGFic29sdXRlIGJhbmRzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIHJlbGF0aXZlIGJvdW5kcyB3aGljaCBhcmUgc2VnbWVudGVkIGludG8gMTAlIGNodW5rcyBvZiBhbGwgdGltZSBtYXhpbXVtIHZlbG9jaXR5IGZvciBlYWNoIHBsYXllciBpcyBhIGxvdCBtb3JlIGluZGljYXRpdmUgb2YgZWZmb3J0IGFuZCB0aGVyZWZvcmUgdGhlaXIgcGVyY2VudGFnZSBvZiBtYXhpbXVtIHZlbG9jaXR5IGluIGEgZ2l2ZW4gd2Vlay4gV2UgYXJlIGFibGUgdG8gc2VlIHRob3VnaCB0aGF0IHRoZXJlIGlzIGEgbG90IG9mIG11bHRpY29sbGluZWFyaXR5IHdpdGhpbiB0aGUgcmVsYXRpdmUgYmFuZHMgY2FsY3VsYXRlZC4gDQoNCmBgYHtyfQ0KIyBNb2RlbGluZw0KDQojIEJpZ3MgTW9kZWwNCm1vZGVsX2JpZyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQogICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfYmlnKQ0Kc3VtbWFyeShtb2RlbF9iaWcpDQoNCg0KIyBTa2lsbCBNb2RlbA0KbW9kZWxfc2tpbGwgPC0gbG0oYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WMythdmdfVjQrYXZnX1Y1K2F2Z19WNithdmdfVjcrYXZnX1Y4LA0KICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX3NraWxsKQ0Kc3VtbWFyeShtb2RlbF9za2lsbCkNCg0KDQojIENvbWJvIE1vZGVsDQptb2RlbF9jb21ibyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQogICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9jb21ibykNCnN1bW1hcnkobW9kZWxfY29tYm8pDQoNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBhbGwgb2YgdGhlIHByZWRpY3RvcnMgYXMgd2VsbCBhcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaW4gdGhlIG1vZGVsIHdlIGNhbiBzZWUgdGhhdCBhIGxvdCBvZiB0aGUgcHJlZGljdG9ycyBoYXZlIHN1cGVyIHN0cm9uZyBjb3JyZWxhdGlvbnMgd2hpY2ggZWFjaCBvdGhlciwgc29tZSBtb3JlIHNvIHRoYW4gdGhleSBhcmUgd2l0aCB0aGUgcmVzcG9uc2UuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgaXNzdWUgd2l0aCB0aGUgbW9kZWxzIGFib3ZlIGlzIHRoYXQgbXVsdGljb2xsaW5lYXJpdHkgaXMgYm9nZ2luZyB0aGVtIGRvd24gYW5kIHdlIGFyZSBub3Qgc2VlaW5nIHRoZSB0cnVlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIHJlc3BvbnNlLiANCg0KYGBge3IgTG9va2luZyBhdCBjb2xsaW5lYXIgcHJlZGljdG9yc30NCmNvcihzcHJpbnRfZXhwb3N1cmVfYmlnWywxMzoyMF0pDQpjb3Ioc3ByaW50X2V4cG9zdXJlX3NraWxsWywxMzoyMF0pDQpjb3Ioc3ByaW50X2V4cG9zdXJlX2NvbWJvWywxMzoyMF0pDQpgYGANCg0KQWxsIG9mIHRoZSBtb2RlbHMgYmVsb3cgYXJlIHRoZSBiZXN0IDMgcHJlZGljdG9yIG1vZGVscyB0aGF0IHJlc3VsdGVkIGZvciBlYWNoIHBvc2l0aW9uIGFmdGVyIHJ1bm5pbmcgdGhlIGJlc3Qgc3Vic2V0cyBhbGdvcml0aG0uIEFsbCBvZiB0aGUgZm9sbG93aW5nIGhhdmUgcmVkdWN0aW9ucyBpbiBhZGp1c3RlZC0kUl4yJCBidXQgdGhleSBkb24ndCBzZWVtIHNpZ25pZmljYW50IGNvbnNpZGVyaW5nIHRoYXQgd2UgdG9vayBvdXQgb3ZlciBoYWxmIG9mIHRoZSBwcmVkaWN0b3JzIGFuZCBtYWludGFpbmVkIGEgcmVsYXRpdmVseSBzaW1pbGFyIGFkanVzdGVkLSRSXjIkIHZhbHVlLiANCg0KYGBge3IgcnVubmluZyBiZXN0IHN1YnNldHMgb24gdGhlIG1vZGVscyB0byBsaW1pdCBjb2xsaW5lYXJpdHl9DQojQmlncw0KDQojYmVzdF9zdWJfYmlnIDwtIHJlZ3N1YnNldHMoYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WMythdmdfVjQrYXZnX1Y1K2F2Z19WNithdmdfVjcrYXZnX1Y4LA0KIyAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcsIG1ldGhvZD0iZXhoYXVzdGl2ZSIpDQojc3VtbWFyeShiZXN0X3N1Yl9iaWcpDQoNCm1vZGVsX2JpZyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1Y2K2F2Z19WNywNCiAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcpDQpzdW1tYXJ5KG1vZGVsX2JpZykNCg0KDQojU2tpbGxzDQoNCiNiZXN0X3N1Yl9za2lsbCA8LSByZWdzdWJzZXRzKGF2Z19wY3RfbWF4fmF2Z19WMithdmdfVjMrYXZnX1Y0K2F2Z19WNSthdmdfVjYrYXZnX1Y3K2F2Z19WOCwNCiMgICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfc2tpbGwsIG1ldGhvZD0iZXhoYXVzdGl2ZSIpDQojc3VtbWFyeShiZXN0X3N1Yl9za2lsbCkNCg0KbW9kZWxfc2tpbGwgPC0gbG0oYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WNSthdmdfVjYsDQogICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfc2tpbGwpDQpzdW1tYXJ5KG1vZGVsX3NraWxsKQ0KDQojQ29tYm9zDQojYmVzdF9zdWJfY29tYm8gPC0gcmVnc3Vic2V0cyhhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQojICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX2NvbWJvLCBtZXRob2Q9ImV4aGF1c3RpdmUiKQ0KI3N1bW1hcnkoYmVzdF9zdWJfY29tYm8pDQoNCm1vZGVsX2NvbWJvIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19WMithdmdfVjQrYXZnX1Y1LA0KICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfY29tYm8pDQpzdW1tYXJ5KG1vZGVsX2NvbWJvKQ0KYGBgDQpTaW5jZSB0aGUgbW9kZWxzIHBlcmZvcm0gcm91Z2hseSB0aGUgc2FtZSB3aXRoIGp1c3QgMyBwcmVkaWN0b3JzIGFzIHRoZXkgZG8gd2l0aCBhbGwgNywgaXQgbWF5IG1ha2Ugc2Vuc2UgdG8gaW5zdGVhZCB0cnVuY2F0ZSBpbnRvIG1vcmUgZ2VuZXJhbCBiaW5zLiBUaGlzIG1heSBoZWxwIHVzIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCB3aXRoIGxvdywgbWVkaXVtIGFuZCBoaWdoIGVmZm9ydCB3aXRoIHdlZWtseSBtYXhpbXVtIHZlbG9jaXR5LiANCg0KYGBge3J9DQpzcHJpbnRfZXhwb3N1cmVfYmlnIDwtIHNwcmludF9leHBvc3VyZV9iaWcgJT4lDQogIG11dGF0ZShhdmdfbG93ID0gKGF2Z19WMithdmdfVjMpLzIsDQogICAgICAgICBhdmdfbWVkaXVtID0gKGF2Z19WNCthdmdfVjUrYXZnX1Y2KS8zLA0KICAgICAgICAgYXZnX2hpZ2ggPSAoYXZnX1Y3K2F2Z19WOCkvMikNCg0Kc3ByaW50X2V4cG9zdXJlX3NraWxsIDwtIHNwcmludF9leHBvc3VyZV9za2lsbCAlPiUNCiAgbXV0YXRlKGF2Z19sb3cgPSAoYXZnX1YyK2F2Z19WMykvMiwNCiAgICAgICAgIGF2Z19tZWRpdW0gPSAoYXZnX1Y0K2F2Z19WNSthdmdfVjYpLzMsDQogICAgICAgICBhdmdfaGlnaCA9IChhdmdfVjcrYXZnX1Y4KS8yKQ0KDQpzcHJpbnRfZXhwb3N1cmVfY29tYm8gPC0gc3ByaW50X2V4cG9zdXJlX2NvbWJvICU+JQ0KICBtdXRhdGUoYXZnX2xvdyA9IChhdmdfVjIrYXZnX1YzKS8yLA0KICAgICAgICAgYXZnX21lZGl1bSA9IChhdmdfVjQrYXZnX1Y1K2F2Z19WNikvMywNCiAgICAgICAgIGF2Z19oaWdoID0gKGF2Z19WNythdmdfVjgpLzIpDQpgYGANCg0KDQpgYGB7cn0NCiNiaWdzDQptb2RlbF9iaWdfZ2VuZXJhbCA8LSBsbShhdmdfcGN0X21heH5hdmdfbG93K2F2Z19tZWRpdW0rYXZnX2hpZ2gsDQogICAgICAgICAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcpDQpzdW1tYXJ5KG1vZGVsX2JpZ19nZW5lcmFsKQ0KDQojc2tpbGxzDQptb2RlbF9za2lsbF9nZW5lcmFsIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19sb3crYXZnX21lZGl1bSthdmdfaGlnaCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX3NraWxsKQ0Kc3VtbWFyeShtb2RlbF9za2lsbF9nZW5lcmFsKQ0KDQojY29tYm9zDQptb2RlbF9jb21ib19nZW5lcmFsIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19sb3crYXZnX21lZGl1bSthdmdfaGlnaCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX2NvbWJvKQ0Kc3VtbWFyeShtb2RlbF9jb21ib19nZW5lcmFsKQ0KYGBgDQpTdGlsbCBwcmV0dHkgYmFkLCBJIGFtIGdvaW5nIHRvIHVzZSBwcmluY2lwbGUgY29tcG9uZW50cyByZWdyZXNzaW9uIHRvIHNlZSBpZiB0aGVyZSBhcmUgb3RoZXIgcmVsYXRpb25zaGlwcyB3ZSBhcmUgbWlzc2luZw0KDQpgYGB7cn0NCnByZWRzIDwtIHNwcmludF9leHBvc3VyZV9iaWdbLDE0OjIwXQ0KDQpwY19sb2FkaW5ncyA8LSBwcmNvbXAocHJlZHMsIHNjYWxlPVRSVUUpJHJvdGF0aW9uWyxjKDEsMiwzKV0NCnBjX2xvYWRpbmdzDQpgYGANCg0KDQojIyBIb3cgZG9lcyBzcHJpbnRpbmcgZXhwb3N1cmUgKCMgb2YgZWZmb3J0cywgJSBtYXggcmVhY2hlZCkgcmVsYXRlIHRvIGluY2lkZW5jZSBvZiBoYW1zdHJpbmcgaW5qdXJpZXM/DQoNCmBgYHtyfQ0KIyBKb2luIHJlbGF0aXZlX2JhbmRzIGRhdGEgc2V0IHdpdGggaW5qdXJ5IGRhdGENCg0KSW5jaWRlbnRfZGF0ZXMgPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkNCg0KIyBHZXQgaWRzIG9mIGF0aGxldGVzIHdpdGggaW5qdXJ5DQppbmp1cmVkX2lkcyA8LSB1bmlxdWUoSW5jaWRlbnRfZGF0ZXMkYW5vbl9pZCkNCg0KIyBGaWx0ZXIgcmVsYXRpdmVfYmFuZHMgdG8gb25seSBpbmNsdWRlIGF0aGxldGVzIHdobyBnb3QgaW5qdXJlZA0KaW5qdXJlZF9kYXRhIDwtIHJlbGF0aXZlX2JhbmRzX2xvbmcgJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9pZHMpDQoNCiMgQ3JlYXRlIGluanVyeSB3ZWVrcw0KaW5qdXJ5X3dlZWtzIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKGFzLkRhdGUoRGF0ZS5vZi5Jbmp1cnkpLCB1bml0ID0gIndlZWsiKQ0KICApICU+JQ0KICBmaWx0ZXIoRGF0ZS5vZi5Jbmp1cnkgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZS5vZi5Jbmp1cnkgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgc2VsZWN0KGFub25faWQsIHdlZWspICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBtdXRhdGUoaW5qdXJ5ID0gMSkNCg0KaW5qdXJpZXNfYW5kX3J1bm5pbmcgPC0gaW5qdXJlZF9kYXRhICU+JQ0KICBsZWZ0X2pvaW4oaW5qdXJ5X3dlZWtzLCBieSA9IGMoImFub25faWQiLCAid2VlayIpKSAlPiUNCiAgbXV0YXRlKGluanVyeSA9IGlmZWxzZShpcy5uYShpbmp1cnkpLCAwLCBpbmp1cnkpKQ0KYGBgDQoNCg0KYGBge3J9DQoNCmluanVyaWVzX2FuZF9ydW5uaW5nIDwtIGluanVyaWVzX2FuZF9ydW5uaW5nICU+JQ0KICBtdXRhdGUocmVsYXRpdmVfYmFuZCA9IGZhY3RvcihyZWxhdGl2ZV9iYW5kLCBsZXZlbHMgPSBiYW5kX2xldmVscykpDQoNCg0KIyBGaWx0ZXIgdG8gb25seSBpbmNsdWRlIHBsYXllcnMgMTk0IGFuZCAyODUNCnBsb3RfZGF0YSA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBjKCJJRF8xOTQiLCAiSURfMjg1IikpDQoNCiMgSWRlbnRpZnkgaW5qdXJ5IHdlZWtzIGZvciBzaGFkaW5nDQppbmp1cnlfc2hhZGluZyA8LSBwbG90X2RhdGEgJT4lDQogIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogIG11dGF0ZSgNCiAgICB4bWluID0gd2VlayAtIDMsDQogICAgeG1heCA9IHdlZWsgKyAzLA0KICAgIHltaW4gPSAtSW5mLA0KICAgIHltYXggPSBJbmYNCiAgKQ0KDQojIENyZWF0ZSBhIHNpbmdsZSBmYWNldGVkIHBsb3QNCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gd2VlaykpICsNCiAgZ2VvbV9yZWN0KGRhdGEgPSBpbmp1cnlfc2hhZGluZywNCiAgICAgICAgICAgIGFlcyh4bWluID0geG1pbiwgeG1heCA9IHhtYXgsIHltaW4gPSB5bWluLCB5bWF4ID0geW1heCksDQogICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKw0KICBnZW9tX2NvbChhZXMoeSA9IHBjdF9lZmZvcnQsIGZpbGwgPSByZWxhdGl2ZV9iYW5kKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayhyZXZlcnNlID0gVFJVRSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICBmYWNldF93cmFwKH4gYW5vbl9pZCwgc2NhbGVzID0gImZyZWVfeCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgdmFsdWVzID0gYygNCiAgICAgICJWMiAoMjAtNDAlKSIgPSAiZGFya2dyZWVuIiwNCiAgICAgICJWMyAoNDAtNTAlKSIgPSAiZ3JlZW4yIiwNCiAgICAgICJWNCAoNTAtNjAlKSIgPSAiZ3JlZW55ZWxsb3ciLA0KICAgICAgIlY1ICg2MC03MCUpIiA9ICJ5ZWxsb3ciLA0KICAgICAgIlY2ICg3MC04MCUpIiA9ICJvcmFuZ2UiLA0KICAgICAgIlY3ICg4MC05MCUpIiA9ICJ0b21hdG8iLA0KICAgICAgIlY4ICg5MC0xMDAlKSIgPSAiZGFya3JlZCINCiAgICApDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbmFtZSA9ICJQZXJjZW50IG9mIFdlZWtseSBFZmZvcnQiLA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuLCBuYW1lID0gIlBlcmNlbnQgb2YgTWF4IFZlbG9jaXR5IikNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVmVsb2NpdHkgQmFuZCBFeHBvc3VyZSAmIE1heCAlIGZvciBJbmp1cmVkIFBsYXllcnMgMTk0IGFuZCAyODUiLA0KICAgIHggPSAiV2VlayIsDQogICAgZmlsbCA9ICJSZWxhdGl2ZSBWZWxvY2l0eSBCYW5kIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KDQpgYGB7cn0NCiMgTG9vcCBvdmVyIGluanVyZWQgYXRobGV0ZXMgYW5kIGNyZWF0ZSB0aGVpciBwbG90cw0KDQp1bmlxdWUoaW5qdXJpZXNfYW5kX3J1bm5pbmckYW5vbl9pZCkgJT4lDQogIGxhcHBseShmdW5jdGlvbihwbGF5ZXJfaWQpIHsNCiAgICANCiAgICAjIEZpbHRlciBkYXRhIGZvciB0aGlzIHBsYXllcg0KICAgIHBsb3RfZGF0YSA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgICAgIGZpbHRlcihhbm9uX2lkID09IHBsYXllcl9pZCkNCiAgICANCiAgICBpZiAobnJvdyhwbG90X2RhdGEpID09IDApIHJldHVybihOVUxMKSAgIyBTa2lwIGlmIG5vIGRhdGENCiAgICANCiAgICAjIEdldCBpbmp1cnkgd2Vla3MNCiAgICBpbmp1cnlfd2Vla19udW1zIDwtIHBsb3RfZGF0YSAlPiUNCiAgICAgIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogICAgICBwdWxsKHdlZWspDQogICAgDQogICAgIyBQbG90DQogICAgcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IHdlZWspKSArDQogICAgICAjIEhpZ2hsaWdodCBpbmp1cnkgd2Vla3Mgd2l0aCBzaGFkZWQgYmx1ZSBhcmVhcw0KICAgICAgZ2VvbV9yZWN0KGRhdGEgPSBkYXRhLmZyYW1lKHdlZWsgPSBpbmp1cnlfd2Vla19udW1zKSwNCiAgICAgICAgICAgICAgICBhZXMoeG1pbiA9IHdlZWsgLSAzLCB4bWF4ID0gd2VlayArIDMsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwNCiAgICAgICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKw0KICAgICAgZ2VvbV9jb2woYWVzKHkgPSBwY3RfZWZmb3J0LCBmaWxsID0gcmVsYXRpdmVfYmFuZCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpKSArDQogICAgICBnZW9tX2xpbmUoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBncm91cCA9IDEpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjIpICsNCiAgICAgIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgIlYyICgyMC00MCUpIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICJWMyAoNDAtNTAlKSIgPSAiZ3JlZW4yIiwNCiAgICAgICAgICAiVjQgKDUwLTYwJSkiID0gImdyZWVueWVsbG93IiwNCiAgICAgICAgICAiVjUgKDYwLTcwJSkiID0gInllbGxvdyIsDQogICAgICAgICAgIlY2ICg3MC04MCUpIiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICJWNyAoODAtOTAlKSIgPSAidG9tYXRvIiwNCiAgICAgICAgICAiVjggKDkwLTEwMCUpIiA9ICJkYXJrcmVkIg0KICAgICAgICApDQogICAgICApICsNCiAgICAgIHNjYWxlX3lfY29udGludW91cygNCiAgICAgICAgbmFtZSA9ICJQZXJjZW50IG9mIFdlZWtseSBFZmZvcnQiLA0KICAgICAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4gLiwgbmFtZSA9ICJQZXJjZW50IG9mIE1heCBWZWxvY2l0eSIpDQogICAgICApICsNCiAgICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gcGFzdGUoIlZlbG9jaXR5IEJhbmQgRXhwb3N1cmUgJiBNYXggJSBmb3IgSW5qdXJlZCBQbGF5ZXIiLCBwbGF5ZXJfaWQpLA0KICAgICAgICB4ID0gIldlZWsiLA0KICAgICAgICBmaWxsID0gIlJlbGF0aXZlIFZlbG9jaXR5IEJhbmQiDQogICAgICApICsNCiAgICAgIHRoZW1lX2NsYXNzaWMoKQ0KICAgIA0KICAgIHByaW50KHBsb3QpDQogICAgDQogICAgcmV0dXJuKE5VTEwpDQogIH0pDQpgYGANClRoZSBibHVlIGxpbmVzIGluZGljYXRlIGEgd2VlayB3aGVyZSBhbiBpbmp1cnkgb2NjdXJyZWQuDQpObyByZWFsIGluc2lnaHRzIGNhbiBiZSBtYWRlIGZyb20gbG9va2luZyBhdCByZWxhdGl2ZSB2ZWxvY2l0eSBiYW5kcyBhbmQgaW5qdXJ5IG9jY3VycmFuY2VzLiANCg0KYGBge3J9DQptb2RlbF9pbmp1cnkgPC0gZ2xtKGluanVyeSB+IHBjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbmp1cmllc19hbmRfcnVubmluZywNCiAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIikNCg0Kc3VtbWFyeShtb2RlbF9pbmp1cnkpDQpgYGANClNpbXBsZSBtb2RlbA0KRWFjaCAxIHBlcmNlbnRhZ2UgcG9pbnQgaW5jcmVhc2UgaW4gcGN0X29mX21heF92ZWxvY2l0eSBpbmNyZWFzZXMgdGhlIGxvZy1vZGRzIG9mIGluanVyeSBieSAwLjAwOTIzLg0KTm90IHNpZ25pZmljYW50Lg0KDQoNCmBgYHtyfQ0KIyBHZXQgc3VtbWFyeSBzdGF0cyBmb3IgYm90aCBpbmp1cnkgYW5kIG5vbiBpbmp1cnkNCg0KIyBDbGVhbiBhbmQgZ2V0IGRhdGUgZm9ybWF0DQppbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgbXV0YXRlKHdlZWtfZm9ybWF0dGVkID0gZm9ybWF0KGFzLkRhdGUod2VlayksICIlbS0lZC0lWSIpKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgd2VlaywgLmtlZXBfYWxsID0gVFJVRSkNCg0KIyBGaWx0ZXIgZm9yIG9ubHkgaW5qdXJ5IHdlZWtzDQppbmp1cnlfd2Vla3MgPC0gaW5qdXJpZXNfYW5kX3J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogIG11dGF0ZShpbmp1cnlfZXZlbnQgPSBwYXN0ZTAoYW5vbl9pZCwgIl9fIiwgd2Vla19mb3JtYXR0ZWQpKQ0KDQojIENhbGN1bGF0ZSBzdGF0aXN0aWNzIGZvciBpbmp1cnkgd2Vla3MNCmluanVyeV9tZWFuIDwtIG1lYW4oaW5qdXJ5X3dlZWtzJHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSkNCg0KaW5qdXJ5X3N1bW1hcnlfc3RhdHMgPC0gaW5qdXJ5X3dlZWtzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9wY3QgPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSksDQogICAgc2RfcGN0ID0gc2QocGN0X29mX21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBuID0gc3VtKCFpcy5uYShwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSkNCiAgKQ0KIyBDb25maWRlbmNlIGludGVydmFsIA0Kc2UgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkc2RfcGN0IC8gc3FydChpbmp1cnlfc3VtbWFyeV9zdGF0cyRuKQ0KdF9jcml0IDwtIHF0KDAuOTc1LCBkZiA9IGluanVyeV9zdW1tYXJ5X3N0YXRzJG4gLSAxKQ0KbG93ZXIgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkbWVhbl9wY3QgLSB0X2NyaXQgKiBzZQ0KdXBwZXIgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkbWVhbl9wY3QgKyB0X2NyaXQgKiBzZQ0KIyBQcmludA0KY2F0KCI5NSUgQ0kgZm9yIG1lYW4gcGN0X29mX21heF92ZWxvY2l0eToiLCByb3VuZChsb3dlciwyKSwgIi0iLCByb3VuZCh1cHBlciwyKSkNCg0KDQojIEZpbHRlciBmb3Igbm9uLWluanVyeSB3ZWVrcw0Kbm9uX2luanVyeV93ZWVrcyA8LSBpbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGluanVyeSA9PSAwKQ0KDQojIEdldCBzdGF0aXN0aWNzIGZvciBub24taW5qdXJ5IHdlZWtzDQpub25faW5qdXJ5X21lYW4gPC0gbWVhbihub25faW5qdXJ5X3dlZWtzJHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSkNCg0Kbm9uX2luanVyeV9zdW1tYXJ5X3N0YXRzIDwtIG5vbl9pbmp1cnlfd2Vla3MgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBtZWFuX3BjdCA9IG1lYW4ocGN0X29mX21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9wY3QgPSBzZChwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBzdW0oIWlzLm5hKHBjdF9vZl9tYXhfdmVsb2NpdHkpKQ0KICApDQoNCiMgQ29uZmlkZW5jZSBpbnRlcnZhbA0Kbm9uX2luanVyeV9zZSA8LSBub25faW5qdXJ5X3N1bW1hcnlfc3RhdHMkc2RfcGN0IC8gc3FydChub25faW5qdXJ5X3N1bW1hcnlfc3RhdHMkbikNCm5vbl9pbmp1cnlfdF9jcml0IDwtIHF0KDAuOTc1LCBkZiA9IG5vbl9pbmp1cnlfc3VtbWFyeV9zdGF0cyRuIC0gMSkNCm5vbl9pbmp1cnlfbG93ZXIgPC0gbm9uX2luanVyeV9zdW1tYXJ5X3N0YXRzJG1lYW5fcGN0IC0gbm9uX2luanVyeV90X2NyaXQgKiBub25faW5qdXJ5X3NlDQpub25faW5qdXJ5X3VwcGVyIDwtIG5vbl9pbmp1cnlfc3VtbWFyeV9zdGF0cyRtZWFuX3BjdCArIG5vbl9pbmp1cnlfdF9jcml0ICogbm9uX2luanVyeV9zZQ0KDQojIFByaW50IHJlc3VsdHMNCmNhdCgiOTUlIENJIGZvciBtZWFuIHBjdF9vZl9tYXhfdmVsb2NpdHkgKG5vbi1pbmp1cnkgd2Vla3MpOiIsIA0KICAgIHJvdW5kKG5vbl9pbmp1cnlfbG93ZXIsIDIpLCAiLSIsIHJvdW5kKG5vbl9pbmp1cnlfdXBwZXIsIDIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBULXRlc3QgdG8gY29tcGFyZSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IGluIGluanVyaWVzIGFuZCBub24gaW5qdXJpZXMgdGhhdCB3ZWVrDQp0X3Rlc3QgPC0gdC50ZXN0KA0KICBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IH4gaW5qdXJ5LCANCiAgZGF0YSA9IGluanVyaWVzX2FuZF9ydW5uaW5nX2NsZWFuLA0KICB2YXIuZXF1YWwgPSBGQUxTRSAjIHVzZSBUUlVFIGlmIHlvdSBhc3N1bWUgZXF1YWwgdmFyaWFuY2VzDQopDQoNCiMgUHJpbnQgcmVzdWx0cw0KcHJpbnQodF90ZXN0KQ0KYGBgDQpHcm91cCAxIChpbmp1cnkpIG1lYW4gPSA4Ni41NQ0KR3JvdXAgMiAobm9uLWluanVyeSkgbWVhbiA9IDg1Ljc0DQpXaGlsZSB0aGUgbWVhbiAlIG9mIG1heCB2ZWxvY2l0eSBpcyBoaWdoZXIgZm9yIHRoZSBpbmp1cnkgZ3JvdXAsIHRoZSBkaWZmZXJlbmNlIGlzIHNtYWxsIGFuZCBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gKGhpZ2ggcC12YWx1ZSwgMC42OSkNCg0KUGxvdHMgb2YgUGVyY2VudCBvZiBNYXggVmVsb2NpdHkgaW4gdGhhdCB3ZWVrDQpgYGB7cn0gIA0KIyBQbG90IG9mICUgTWF4IFZlbG9jaXR5IGR1cmluZyBpbmp1cnkgYW5kIG5vbiBpbmp1cnkgd2Vla3MNCmdncGxvdChpbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiwgYWVzKHggPSB3ZWVrLCB5ID0gcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoaW5qdXJ5KSkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gImJsYWNrIiwgIjEiID0gInJlZCIpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm8gSW5qdXJ5IiwgIkluanVyeSIpLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJbmp1cnkgU3RhdHVzIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBlcmNlbnQgb2YgTWF4IFZlbG9jaXR5IGJ5IFdlZWsiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIk1lYW4gJSBvZiBNYXggVmVsb2NpdHksIEluanVyeTogIiwgcm91bmQoaW5qdXJ5X21lYW4sIDIpLCAiIHwgIE1lYW4gJSBvZiBNYXggVmVsb2NpdHksIE5vbi1Jbmp1cnk6ICIsIHJvdW5kKG5vbl9pbmp1cnlfbWVhbiwgMikpLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICIlIG9mIE1heCBWZWxvY2l0eSBSZWFjaGVkIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCg0KZ2dwbG90KGluanVyeV93ZWVrcywgYWVzKHggPSB3ZWVrLCB5ID0gcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkgRHVyaW5nIEluanVyeSBXZWVrcyIsDQogICAgc3VidGl0bGUgPSBwYXN0ZSgiJSBvZiBNYXggVmVsb2NpdHkgTWVhbjogIiwgcm91bmQoaW5qdXJ5X21lYW4sIDIpKSwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiJSBvZiBNYXggVmVsb2NpdHkiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBDcmVhdGUgYmFyIGNoYXJ0DQpnZ3Bsb3QoaW5qdXJ5X3dlZWtzLCBhZXMoeCA9IGZhY3Rvcihpbmp1cnlfZXZlbnQpLCB5ID0gcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQocGN0X29mX21heF92ZWxvY2l0eSwgMSkpLCAgIyByb3VuZCB0byAxIGRlY2ltYWwgcGxhY2UNCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIuNzUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQZXJjZW50IG9mIE1heCBWZWxvY2l0eSBEdXJpbmcgSW5qdXJ5IFdlZWtzIiwNCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCJNZWFuOiAiLCByb3VuZChpbmp1cnlfbWVhbiwgMikpLA0KICAgIHggPSAiSW5qdXJ5IEV2ZW50IiwNCiAgICB5ID0gIiUgb2YgTWF4IFZlbG9jaXR5Ig0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQpgYGB7cn0NCiMgTG9va2luZyBpbnRvIHdlZWtzIGJlZm9yZSBpbmp1cnkNCg0KIyBDYWxjdWxhdGUgcGVyY2VudCBvZiBtYXggdmVsb2NpdHkgZnJvbSAxLCAyLCBhbmQgMyB3ZWVrcyBwcmlvciBhcyB3ZWxsIGFzIHRoZSBzdW0gb2Ygd2Vla3MgMSBhbmQgMiBhbmQgdGhlIHN1bSBvZiB3ZWVrcyAxLCAyLCBhbmQgMyBhcyB3ZWxsIGFzIHRoZSBjaGFuZ2UgYmV0d2VlbiB3ZWVrIG9uZSBhbmQgd2VlayAyDQp3ZWVrX3ByaW9yIDwtIGluanVyaWVzX2FuZF9ydW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgYXJyYW5nZSh3ZWVrKSAlPiUNCiAgbXV0YXRlKA0KICAgICMgUHJldmlvdXMgd2Vla3MgdmFsdWVzIG9mICUgb2YgbWF4IHZlbG9jaXR5DQogICAgbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gbGFnKHBjdF9vZl9tYXhfdmVsb2NpdHksIDEpLA0KICAgIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSA9IGxhZyhwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCAyKSwNCiAgICBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHkgPSBsYWcocGN0X29mX21heF92ZWxvY2l0eSwgMyksDQogICAgDQogICAgIyBDaGFuZ2Ugb2YgJSBvZiBtYXggZnJvbSBjdXJyZW50IHdlZWsgdG8gcHJldmlvdXMgd2Vla3MNCiAgICBjaGFuZ2VfbGFzdHdlZWsgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IC0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIGNoYW5nZV9sYXN0MndlZWtzID0gcGN0X29mX21heF92ZWxvY2l0eSAtIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICBjaGFuZ2VfbGFzdDN3ZWVrcyA9IHBjdF9vZl9tYXhfdmVsb2NpdHkgLSBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgDQogICAgIyBTdW0gJSBvZiBtYXggb2YgY3VycmVudCB0byBwcmV2aW91cyB3ZWVrcw0KICAgIHN1bV9sYXN0d2VlayA9IHBjdF9vZl9tYXhfdmVsb2NpdHkgKyBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgc3VtX2xhc3Qyd2Vla3MgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIHN1bV9sYXN0M3dlZWtzID0gcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzNfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICANCiAgICAjIENoYW5nZSBiZXR3ZWVuIHdlZWtzIChub3QgY3VycmVudCB3ZWVrKQ0KICAgIGNoYW5nZV93ZWVrczFfMiA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSAtIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICBjaGFuZ2Vfd2Vla3MxXzMgPSBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkgLSBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgDQogICAgIyBTdW0gb2Ygd2Vla3MgKG5vdCBjdXJyZW50IHdlZWspDQogICAgc3VtX3dlZWtzMV8yID0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIHN1bV93ZWVrczFfMl8zID0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnM19wY3Rfb2ZfbWF4X3ZlbG9jaXR5DQogICAgKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgR2V0IEluanVyeSBFdmVudCB2YXJpYWJsZSBhbmQgSW5qdXJ5IGRhdGFzZXQNCmluanVyeV93ZWVrX3ByaW9yIDwtIHdlZWtfcHJpb3IgJT4lDQogIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogIG11dGF0ZShpbmp1cnlfZXZlbnQgPSBwYXN0ZTAoYW5vbl9pZCwgIl9fIiwgd2Vla19mb3JtYXR0ZWQpKQ0KDQojIEdldCBub24taW5qdXJ5IGRhdGFzZXQNCm5vbl9pbmp1cnlfd2Vla19wcmlvciA8LSB3ZWVrX3ByaW9yICU+JQ0KICBmaWx0ZXIoaW5qdXJ5ID09IDApDQoNCiMgR2V0IE1lYW5zDQptZWFuc19pbmp1cnkgPC0gaW5qdXJ5X3dlZWtfcHJpb3IgJT4lDQogIGRyb3BfbmEobGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KQ0KaW5qdXJ5X3dlZWtfcHJpb3JfbWVhbiA8LSBtZWFuKG1lYW5zX2luanVyeSRsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQptZWFuX25vbmluanVyeSA8LSBub25faW5qdXJ5X3dlZWtfcHJpb3IgJT4lDQogIGRyb3BfbmEobGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KQ0Kbm9uX2luanVyeV93ZWVrX3ByaW9yX21lYW4gPC0gbWVhbihtZWFuX25vbmluanVyeSRsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQppbmp1cnlfd2Vla19wcmlvcl9tZWFuDQpub25faW5qdXJ5X3dlZWtfcHJpb3JfbWVhbg0KDQoNCg0KIyBQbG90IG9mICUgTWF4IFZlbG9jaXR5IGR1cmluZyBpbmp1cnkgYW5kIG5vbiBpbmp1cnkgd2Vla3MNCmdncGxvdCh3ZWVrX3ByaW9yLCBhZXMoeCA9IHdlZWssIHkgPSBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGluanVyeSkpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIwIiA9ICJibGFjayIsICIxIiA9ICJyZWQiKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5vIEluanVyeSIsICJJbmp1cnkiKSwNCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSW5qdXJ5IFN0YXR1cyIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQZXJjZW50IG9mIE1heCBWZWxvY2l0eSBvZiBQcmlvciBXZWVrIiwNCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCJNZWFuICUgb2YgTWF4IFZlbG9jaXR5LCBJbmp1cnk6ICIsIHJvdW5kKGluanVyeV93ZWVrX3ByaW9yX21lYW4sIDIpLCAiIHwgIE1lYW4gJSBvZiBNYXggVmVsb2NpdHksIE5vbi1Jbmp1cnk6ICIsIHJvdW5kKG5vbl9pbmp1cnlfd2Vla19wcmlvcl9tZWFuLCAyKSksDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIiUgb2YgTWF4IFZlbG9jaXR5IFJlYWNoZWQiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KDQpnZ3Bsb3QoaW5qdXJ5X3dlZWtfcHJpb3IsIGFlcyh4ID0gd2VlaywgeSA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkgdGhlIHdlZWsgYmVmb3JlIGluanVyeSIsDQogICAgc3VidGl0bGUgPSBwYXN0ZSgiJSBvZiBNYXggVmVsb2NpdHkgTWVhbjogIiwgcm91bmQoaW5qdXJ5X3dlZWtfcHJpb3JfbWVhbiwgMikpLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICIlIG9mIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIEJhciBjaGFydCBmb3Igd2VlayBwcmlvciB0byBpbmp1cnkgJSBNYXgNCmdncGxvdChpbmp1cnlfd2Vla19wcmlvciwgYWVzKHggPSBmYWN0b3IoaW5qdXJ5X2V2ZW50KSwgeSA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQobGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5LCAxKSksICAjIHJvdW5kIHRvIDEgZGVjaW1hbCBwbGFjZQ0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBzaXplID0gMi43NSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBlcmNlbnQgb2YgTWF4IFZlbG9jaXR5IHRoZSBXZWVrIEJlZm9yZSBJbmp1cnkiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIiUgTWF4IE1lYW46ICIsIHJvdW5kKGluanVyeV93ZWVrX3ByaW9yX21lYW4sIDIpKSwNCiAgICB4ID0gIkluanVyeSBFdmVudCIsDQogICAgeSA9ICIlIG9mIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBULXRlc3QgdG8gY29tcGFyZSBsYWdfcGN0X29mX21heF92ZWxvY2l0eSAobWF4IHZlbG9jaXR5IHRoZSB3ZWVrIGJlZm9yZSkgYmV0d2VlbiBpbmp1cnkgYW5kIG5vbiBpbmp1cnkNCnRfdGVzdF93ZWVrX3ByaW9yIDwtIHQudGVzdCgNCiAgbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5IH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X3dlZWtfcHJpb3IpDQoNCiMgVC10ZXN0IHRvIGNvbXBhcmUgY2hhbmdlIGJldHdlZW4gY3VycmVudCB3ZWVrIGFuZCBsYXN0IHdlZWsNCnRfdGVzdF9jaGFuZ2VfbGFzdHdlZWsgPC0gdC50ZXN0KA0KICBjaGFuZ2VfbGFzdHdlZWsgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3RfY2hhbmdlX2xhc3R3ZWVrKQ0KDQojIFQtdGVzdCB0byBjb21wYXJlIGNoYW5nZSBiZXR3ZWVuIGN1cnJlbnQgd2VlayBhbmQgMiB3ZWVrcyBhZ28NCnRfdGVzdF9jaGFuZ2VfbGFzdDJ3ZWVrcyA8LSB0LnRlc3QoDQogIGNoYW5nZV9sYXN0MndlZWtzIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X2NoYW5nZV9sYXN0MndlZWtzKQ0KDQojIFQtdGVzdCB0byBjb21wYXJlIGNoYW5nZSBiZXR3ZWVuIGN1cnJlbnQgd2VlayBhbmQgMyB3ZWVrcyBhZ28NCnRfdGVzdF9jaGFuZ2VfbGFzdDN3ZWVrcyA8LSB0LnRlc3QoDQogIGNoYW5nZV9sYXN0M3dlZWtzIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X2NoYW5nZV9sYXN0M3dlZWtzKQ0KDQoNCiMgVC10ZXN0IHRvIGNvbXBhcmUgc3VtIG9mIGN1cnJlbnQgd2VlaywgMSwgMiwgYW5kIDMgd2Vla3MgYmVmb3JlDQp0X3Rlc3Rfc3VtX2xhc3Qzd2Vla3MgPC0gdC50ZXN0KA0KICBzdW1fbGFzdDN3ZWVrcyB+IGluanVyeSwgDQogIGRhdGEgPSB3ZWVrX3ByaW9yLA0KICB2YXIuZXF1YWwgPSBGQUxTRSkNCnByaW50KHRfdGVzdF9zdW1fbGFzdDN3ZWVrcykNCmBgYA0KTWVhbiAlIG9mIG1heCB3ZWVrIGJlZm9yZTogaW5qdXJ5ID0gODYuNiwgbm9uLWluanVyeSA9IDg2LjIuDQpOb3Qgc2lnLCBwdmFsdWUgPSAwLjg3DQoNCk1lYW4gJSBjaGFuZ2Ugd2VlayBiZWZvcmUgKGN1cnJlbnQgd2VlayAlIC0gbGFzdCB3ZWVrICUpOiBpbmp1cnkgPSAtMC4wNTcsIG5vbi1pbmp1cnkgPSAtMC41MA0KTm90IHNpZywgcC12YWx1ZSA9IDAuODkNCg0KTWVhbiAlIGNoYW5nZSBvZiBsYXN0IDIgd2Vla3MgKGN1cnJlbnQgLSAyIHdlZWtzIGFnbyk6IGluanVyeSA9IC0yLjY4LCBub24taW5qdXJ5ID0gLTAuNjYNCk5vdCBzaWcsIHAtdmFsdWUgPSAwLjQ0DQoNCk1lYW4gJSBjaGFuZ2Ugb2YgbGFzdCAzIHdlZWtzIChjdXJyZW50IC0gMyB3ZWVrcyBhZ28pOiBpbmp1cnkgPSAtMC42MCwgbm9uLWluanVyeSA9IC0wLjgxLg0KTm90IHNpZywgcC12YWx1ZSA9IDAuOTUNCg0KU3VtIG9mIGN1cnJlbnQsIDEsIDIsIDMgd2Vla3MgYmVmb3JlOg0KaW5qdXJ5ID0gMzQ3Ljg2LCBub24taW5qdXJ5ID0gMzQzLjUxDQpOb3Qgc2lnLCBwLXZhbHVlID0gMC40MQ0KDQpgYGB7cn0NCiMgTG9vayBpbnRvIHRoZSBsYXN0IHR3byB3ZWVrcw0KDQojIERyb3AgTkFzDQpsYWdfMl9pbmp1cnkgPC0gaW5qdXJ5X3dlZWtfcHJpb3IgJT4lDQogIGRyb3BfbmEobGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KQ0KbGFnXzJfbm9uX2luanVyeSA8LSBub25faW5qdXJ5X3dlZWtfcHJpb3IgJT4lDQogIGRyb3BfbmEobGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KQ0KDQojIENhbGN1bGF0ZSBtZWFucw0KbWVhbl9sYWcyX2luanVyeSA8LSBtZWFuKGxhZ18yX2luanVyeSRsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQptZWFuX2xhZzJfbm9uX2luanVyeSA8LSBtZWFuKGxhZ18yX25vbl9pbmp1cnkkbGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KQ0KbWVhbl9sYWcyX2luanVyeQ0KbWVhbl9sYWcyX25vbl9pbmp1cnkNCg0KIyBUd28gd2Vla3MgYmVmb3JlOg0KIyBULXRlc3QgdG8gY29tcGFyZSBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkgKDIgd2Vla3MgYmVmb3JlKSBiZXR3ZWVuIGluanVyeSBhbmQgbm9uIGluanVyeQ0KdF90ZXN0XzJ3ZWVrX3ByaW9yIDwtIHQudGVzdCgNCiAgbGFnMl9wY3Rfb2ZfbWF4X3ZlbG9jaXR5IH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0XzJ3ZWVrX3ByaW9yKQ0KDQojIENoYW5nZSBvZiBsYXN0IHR3byB3ZWVrcyAobGFnMSAtIGxhZzIpDQp0X3Rlc3RfY2hhbmdlX3dlZWtzMV8yIDwtIHQudGVzdCgNCiAgY2hhbmdlX3dlZWtzMV8yIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X2NoYW5nZV93ZWVrczFfMikNCg0KIyBTdW0gb2Ygd2Vla3MgMSBhbmQgMg0KdF90ZXN0X3N1bV93ZWVrczFfMiA8LSB0LnRlc3QoDQogIHN1bV93ZWVrczFfMiB+IGluanVyeSwgDQogIGRhdGEgPSB3ZWVrX3ByaW9yLA0KICB2YXIuZXF1YWwgPSBGQUxTRSkNCnByaW50KHRfdGVzdF9zdW1fd2Vla3MxXzIpDQoNCg0KIyAzIHdlZWtzIGJlZm9yZQ0KIyBULXRlc3QgdG8gY29tcGFyZSBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHkgKDMgd2Vla3MgYmVmb3JlKSBiZXR3ZWVuIGluanVyeSBhbmQgbm9uIGluanVyeQ0KdF90ZXN0XzN3ZWVrX3ByaW9yIDwtIHQudGVzdCgNCiAgbGFnM19wY3Rfb2ZfbWF4X3ZlbG9jaXR5IH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0XzN3ZWVrX3ByaW9yKQ0KDQojIENoYW5nZSBiZXR3ZWVuIDEgd2Vla3MgYmVmb3JlIGFuZCAzIHdlZWtzIGJlZm9yZQ0KdF90ZXN0X2NoYW5nZV93ZWVrczFfMyA8LSB0LnRlc3QoDQogIGNoYW5nZV93ZWVrczFfMyB+IGluanVyeSwgDQogIGRhdGEgPSB3ZWVrX3ByaW9yLA0KICB2YXIuZXF1YWwgPSBGQUxTRSkNCnByaW50KHRfdGVzdF9jaGFuZ2Vfd2Vla3MxXzMpDQoNCiMgU3VtIG9mIDEsIDIsIGFuZCAzIHdlZWtzIGJlZm9yZSBpbmp1cnkNCnRfdGVzdF9zdW1fd2Vla3MxXzJfMyA8LSB0LnRlc3QoDQogIHN1bV93ZWVrczFfMl8zIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X3N1bV93ZWVrczFfMl8zKQ0KYGBgDQoyIHdlZWtzIGJlZm9yZSBpbmp1cnk6DQpNZWFucyBvZiAlIG9mIG1heDogaW5qdXJ5ID0gODguNywgbm9uLWluanVyeSA9IDg1LjgNCk5vdCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gbWVhbnMsIHAtdmFsdWUsIDAuMTgzMyA+IDAuMDUuDQoNCk1lYW5zIG9mIGNoYW5nZTogaW5qdXJ5ID0gLTIuMzQsIG5vbi1pbmp1cnkgPSAwLjEwOA0KTm90IHNpZ25pZmljYW50IChwLXZhbHVlID0gMC40NykNCg0KTWVhbiBvZiBzdW0gb2Ygd2Vla3MgMSBhbmQgMjogaW5qdXJ5ID0gMTc1LjE0LCBub24taW5qdXJ5ID0gMTcxLjcyDQpOb3Qgc2lnbmlmaWNhbnQsIHAtdmFsdWUgPSAwLjMzMw0KDQoNCjMgd2Vla3MgYmVmb3JlIGluanVyeToNCk1lYW5zIG9mICUgb2YgbWF4OiBpbmp1cnkgPSA4Ni43LCBub24taW5qdXJ5ID0gODUuOQ0KTm90IGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBtZWFucywgaGlnaCBwLXZhbHVlLCAwLjcNCg0KTWVhbiBzdW0gb2YgMSwgMiwgYW5kIDMgd2Vla3MgYmVmb3JlIGluanVyeTogaW5qdXJ5ID0gMjYxLjgsIG5vbi1pbmp1cnkgPSAyNTcuMw0KTm90IHNpZywgcC12YWx1ZSA9IDAuMzYNCg0KTWVhbiBjaGFuZ2UgYmV0d2VlbiAxIGFuZCAzIHdlZWtzIGJlZm9yZTogaW5qdXJ5ID0gLTAuMjYsIG5vbi1pbmp1cnkgPSAtMC4yNw0KTm90IHNpZywgcC12YWwgPSAwLjk5DQoNCmBgYHtyfQ0KbW9kZWwgPC0gZ2xtKGluanVyeSB+IGNoYW5nZV9sYXN0d2VlayArIGNoYW5nZV9sYXN0MndlZWtzLA0KICAgICAgICAgICAgIGRhdGEgPSB3ZWVrX3ByaW9yLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KVGhlcmUgaXMgbm8gY2xlYXIgZXZpZGVuY2UgdGhhdCAlIG9mIG1heCB2ZWxvY2l0eSBpbiB0aGUgY3VycmVudCBvciBwcmlvciB3ZWVrcyAob3IgdGhlaXIgY2hhbmdlcy9zdW1zKSBkaWZmZXJzIGJldHdlZW4gaW5qdXJ5IGFuZCBub24taW5qdXJ5IHdlZWtzLCBiYXNlZCBvbiB0LXRlc3RzIG9mIG1lYW4gZGlmZmVyZW5jZXMuDQoNCiMjIyMjIENvbXBhcmluZyB0aGUgbnVtYmVyIG9mID45MCUgZWZmb3J0cyB3aXRoIGluanVyeQ0KDQpgYGB7cn0NCiMgQ29udmVydCA5MCBwZXJjZW50IG1heCB0byBsb2dpY2FsDQojIEluc3RlYWQgb2YgWWVzL05vIGl0IGlzIFRydWUvRmFsc2UNCmNhdGFwdWx0X3dlZWtseSA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBtdXRhdGUoDQogICAgRGF0ZSA9IGFzLkRhdGUoRGF0ZSksDQogICAgd2VlayA9IGZsb29yX2RhdGUoRGF0ZSwgdW5pdCA9ICJ3ZWVrIiksDQogICAgSGl0OTAgPSBIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIgICMgQ29udmVydCB0byBsb2dpY2FsDQogICkgJT4lDQogIGRpc3RpbmN0KGFub25faWQsIERhdGUsIC5rZWVwX2FsbCA9IFRSVUUpDQoNCg0KIyBHZXQgYWxsIGNvbWJpbmF0aW9ucyBvZiBwbGF5ZXIgYW5kIHdlZWsNCmFsbF9jb21iaW5hdGlvbnMgPC0gY2F0YXB1bHRfd2Vla2x5ICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCB3ZWVrKQ0KDQojIENvdW50IHdlZWtseSA+OTAlIHNwcmludCBoaXRzDQp3ZWVrbHlfaGl0cyA8LSBjYXRhcHVsdF93ZWVrbHkgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIHdlZWspICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY291bnRfOTBwY3QgPSBzdW0oSGl0OTAsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCiMgSm9pbiB3aXRoIGFsbCBjb21iaW5hdGlvbnMgdG8gaW5jbHVkZSB6ZXJvIGNvdW50cw0Kd2Vla2x5XzkwcGN0IDwtIGFsbF9jb21iaW5hdGlvbnMgJT4lDQogIGxlZnRfam9pbih3ZWVrbHlfaGl0cywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSkgJT4lDQogIG11dGF0ZShjb3VudF85MHBjdCA9IHJlcGxhY2VfbmEoY291bnRfOTBwY3QsIDApKQ0KDQojIFNlZSBicmVha2Rvd24gb2YgY291bnRzIG9mICMgb2YgdGltZXMgPjkwDQpkaXN0cnVidXRpb24gPC0gd2Vla2x5XzkwcGN0ICU+JQ0KICBjb3VudChjb3VudF85MHBjdCkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGluanVyeSB3ZWVrcw0KaW5qdXJ5X3dlZWtzIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKGFzLkRhdGUoRGF0ZS5vZi5Jbmp1cnkpLCB1bml0ID0gIndlZWsiKQ0KICApICU+JQ0KICBmaWx0ZXIoRGF0ZS5vZi5Jbmp1cnkgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZS5vZi5Jbmp1cnkgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgc2VsZWN0KGFub25faWQsIHdlZWspICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBtdXRhdGUoaW5qdXJ5ID0gMSkNCg0KIyBNZXJnZSB3aXRoIGluanVyeSBkYXRhIHRvIGhhdmUgY29sdW1uIHRoYXQgaW5kaWNhdGVzIGluanVyeSB3ZWVrDQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgPC0gd2Vla2x5XzkwcGN0ICU+JQ0KICBsZWZ0X2pvaW4oaW5qdXJ5X3dlZWtzLCBieSA9IGMoImFub25faWQiLCAid2VlayIpKSAlPiUNCiAgbXV0YXRlKGluanVyeSA9IHJlcGxhY2VfbmEoaW5qdXJ5LCAwKSkNCg0KIyBDYWxjdWxhdGUgbGFnZ2VkID45MCUgZWZmb3J0IGNvdW50cw0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgYXJyYW5nZShhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZSgNCiAgICBsYWcxXzkwcGN0X2NvdW50ID0gbGFnKGNvdW50XzkwcGN0LCAxKSwNCiAgICBsYWcyXzkwcGN0X2NvdW50ID0gbGFnKGNvdW50XzkwcGN0LCAyKSwNCiAgICBsYWczXzkwcGN0X2NvdW50ID0gbGFnKGNvdW50XzkwcGN0LCAzKSwNCiAgICANCiAgICBjaGFuZ2VfbGFzdHdlZWsgPSBjb3VudF85MHBjdCAtIGxhZzFfOTBwY3RfY291bnQsDQogICAgY2hhbmdlX2xhc3Qyd2Vla3MgPSBjb3VudF85MHBjdCAtIGxhZzJfOTBwY3RfY291bnQsDQogICAgY2hhbmdlX2xhc3Qzd2Vla3MgPSBjb3VudF85MHBjdCAtIGxhZzNfOTBwY3RfY291bnQsDQogICAgDQogICAgc3VtX2xhc3Qyd2Vla3MgPSBjb3VudF85MHBjdCArIGxhZzFfOTBwY3RfY291bnQsDQogICAgc3VtX2xhc3Qzd2Vla3MgPSBjb3VudF85MHBjdCArIGxhZzFfOTBwY3RfY291bnQgKyBsYWcyXzkwcGN0X2NvdW50LA0KICAgIA0KICAgIGF2Z19sYXN0d2VlayA9IChjb3VudF85MHBjdCArIGxhZzFfOTBwY3RfY291bnQpIC8gMiwNCiAgICBhdmdfbGFzdDJ3ZWVrcyA9IChjb3VudF85MHBjdCArIGxhZzFfOTBwY3RfY291bnQgKyBsYWcyXzkwcGN0X2NvdW50KSAvIDMsDQogICAgYXZnX2xhc3Qzd2Vla3MgPSAoY291bnRfOTBwY3QgKyBsYWcxXzkwcGN0X2NvdW50ICsgbGFnMl85MHBjdF9jb3VudCArIGxhZzNfOTBwY3RfY291bnQpIC8gNA0KICApICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQpgYGB7cn0NCiMgQmFyIGNoYXJ0IG9mIHNwcmludCBjb3VudHMgdGhlIHdlZWsgb2YgYW4gaW5qdXJ5DQoNCiMgT25seSBpbmp1cmllcw0KaW5qdXJ5X3N1bW1hcnkgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBncm91cF9ieShjb3VudF85MHBjdCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICB0b3RhbF9pbmp1cmllcyA9IHN1bShpbmp1cnkpLA0KICAgIHRvdGFsX3dlZWtzID0gbigpDQogICkNCg0KZ2dwbG90KGluanVyeV9zdW1tYXJ5LCBhZXMoeCA9IGZhY3Rvcihjb3VudF85MHBjdCksIHkgPSB0b3RhbF9pbmp1cmllcykpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdG90YWxfaW5qdXJpZXMpLCB2anVzdCA9IC0wLjMsIHNpemUgPSA0KSArDQogIGxhYnMoDQogICAgeCA9ICJXZWVrbHkgPjkwJSBTcHJpbnQgQ291bnQiLA0KICAgIHkgPSAiVG90YWwgSW5qdXJpZXMiLA0KICAgIHRpdGxlID0gIkluanVyaWVzIGJ5ID45MCUgU3ByaW50IENvdW50IGluIHRoZSBXZWVrIG9mIEluanVyeSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBJbmp1cnkgcmF0ZSB0YWJsZSBmb3Igd2VlayBvZiBpbmp1cnkgY291bnRzDQppbmp1cnlfcmF0ZV90YWJsZSA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIGdyb3VwX2J5KGNvdW50XzkwcGN0KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX3dlZWtzID0gbigpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVG90YWwgd2Vla3Mgd2l0aCB0aGlzIGNvdW50DQogICAgaW5qdXJ5X3dlZWtzID0gc3VtKGluanVyeSA9PSAxKSwgICAgICAgICAgICAgIyBIb3cgbWFueSBoYWQgaW5qdXJ5IHRoYXQgd2Vlaw0KICAgIGluanVyeV9yYXRlID0gaW5qdXJ5X3dlZWtzIC8gdG90YWxfd2Vla3MgICAgIyBQcm9wb3J0aW9uIGluanVyZWQNCiAgKSAlPiUNCiAgYXJyYW5nZShkZXNjKGluanVyeV9yYXRlKSkNCmluanVyeV9yYXRlX3RhYmxlDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlIG1hbnVhbGx5DQppbmp1cnlfdGFibGUgPC0gbWF0cml4KGMoDQogIDEwLCAxMzQzLCAgICMgTG93OiBpbmp1cmVkLCBub3QgaW5qdXJlZA0KICA2LCAzOTUsICAgICAjIE1vZGVyYXRlOiBpbmp1cmVkLCBub3QgaW5qdXJlZA0KICAwLCA3OCAgICAgICMgSGlnaDogaW5qdXJlZCwgbm90IGluanVyZWQNCiksIG5yb3cgPSAzLCBieXJvdyA9IFRSVUUpDQoNCiMgQWRkIHJvdyBhbmQgY29sdW1uIG5hbWVzIGZvciBjbGFyaXR5DQpyb3duYW1lcyhpbmp1cnlfdGFibGUpIDwtIGMoIkxvdyIsICJNb2RlcmF0ZSIsICJIaWdoIikNCmNvbG5hbWVzKGluanVyeV90YWJsZSkgPC0gYygiSW5qdXJlZCIsICJOb3RfSW5qdXJlZCIpDQoNCiMgUnVuIEZpc2hlcidzIEV4YWN0IFRlc3QNCmZpc2hlcl9yZXN1bHQgPC0gZmlzaGVyLnRlc3QoaW5qdXJ5X3RhYmxlKQ0KDQojIFZpZXcgdGhlIHJlc3VsdA0KcHJpbnQoZmlzaGVyX3Jlc3VsdCkNCmBgYA0KT3VyIHAtdmFsdWUgb2YgMC4zMyBtZWFucyB0aGF0IHRoZXJlIGlzIG5vIHN0YXRpY2FsbHkgc2lnbmlmaWNhbmNlIGFzc29jaWF0aW9uIGJldHdlZW4gb3VyIHNwcmludCBsb2FkIGdyb3VwcyBhbmQgaW5qdXJ5LiANCkluanVyeSBldmVudHMgYXJlIHByZXR0eSByYXJlIG92ZXJhbGwgaW4gb3VyIGRhdGFzZXQsIG1ha2luZyBpdCBoYXJkIHRvIGRldGVjdCBzdWJ0bGUgZGlmZmVyZW5jZXMuDQoNCg0KYGBge3J9DQojIFN1bW1hcml6ZSBpbmp1cmllcyBhbmQgdG90YWwgd2Vla3MgYnkgdG90YWwgc3ByaW50IGNvdW50cyBvdmVyIGxhc3QgMiB3ZWVrcw0KaW5qdXJ5X3N1bW1hcnlfMndlZWsgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBtdXRhdGUoc3VtX2xhc3Qyd2Vla3MgPSByZXBsYWNlX25hKHN1bV9sYXN0MndlZWtzLCAwKSkgJT4lDQogIGdyb3VwX2J5KHN1bV9sYXN0MndlZWtzKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX2luanVyaWVzID0gc3VtKGluanVyeSA9PSAxKSwNCiAgICB0b3RhbF93ZWVrcyA9IG4oKSwNCiAgICBpbmp1cnlfcmF0ZSA9ICh0b3RhbF9pbmp1cmllcyAvIHRvdGFsX3dlZWtzKSAqIDEwMA0KICApICU+JQ0KICBhcnJhbmdlKGRlc2MoaW5qdXJ5X3JhdGUpKQ0KaW5qdXJ5X3N1bW1hcnlfMndlZWsNCg0KIyBQbG90IHRvdGFsIGluanVyaWVzIGJ5IHRvdGFsIHNwcmludCBjb3VudHMgb3ZlciBsYXN0IDIgd2Vla3MNCmdncGxvdChpbmp1cnlfc3VtbWFyeV8yd2VlaywgYWVzKHggPSBmYWN0b3Ioc3VtX2xhc3Qyd2Vla3MpLCB5ID0gdG90YWxfaW5qdXJpZXMpKSArDQogIGdlb21fY29sKGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRvdGFsX2luanVyaWVzKSwgdmp1c3QgPSAtMC4zLCBzaXplID0gNCkgKw0KICBsYWJzKA0KICAgIHggPSAiVG90YWwgPjkwJSBTcHJpbnQgQ291bnRzIE92ZXIgTGFzdCAyIFdlZWtzIiwNCiAgICB5ID0gIlRvdGFsIEluanVyaWVzIiwNCiAgICB0aXRsZSA9ICJJbmp1cmllcyBieSBUb3RhbCA+OTAlIFNwcmludCBDb3VudHMgT3ZlciB0aGUgTGFzdCBUd28gV2Vla3MiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBQbG90IGluanVyeSByYXRlDQpnZ3Bsb3QoaW5qdXJ5X3N1bW1hcnlfMndlZWssIGFlcyh4PWZhY3RvcihzdW1fbGFzdDJ3ZWVrcyksIHkgPSBpbmp1cnlfcmF0ZSkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoaW5qdXJ5X3JhdGUsIDMpKSwgdmp1c3QgPSAtMC4zLCBzaXplID0gNCkgKw0KICBsYWJzKA0KICAgIHggPSAiVG90YWwgPjkwJSBTcHJpbnQgQ291bnRzIE92ZXIgTGFzdCAyIFdlZWtzIiwNCiAgICB5ID0gIkluanVyeSBSYXRlIiwNCiAgICB0aXRsZSA9ICJJbmp1cmllIFJhdGUgYnkgVG90YWwgPjkwJSBTcHJpbnQgQ291bnRzIE92ZXIgdGhlIExhc3QgVHdvIFdlZWtzIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KYGBge3J9DQojIFRhYmxlIGZvciBjb3VudHMgb3ZlciBsYXN0IGZldyB3ZWVrcw0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgbXV0YXRlKGF2ZzJfZ3JvdXAgPSByb3VuZChhdmdfbGFzdDJ3ZWVrcywgMSkpDQoNCiMgQ3JlYXRlIGJpbm5lZCBncm91cHM6IExvdyAoMOKAkzEpLCBNb2RlcmF0ZSAoMeKAkzIpLCBIaWdoICgyKykNCndlZWtseV85MHBjdF9pbmp1cmllcyA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIG11dGF0ZShsb2FkX2dyb3VwID0gY2FzZV93aGVuKA0KICAgIGF2Z19sYXN0MndlZWtzIDwgMSB+ICJMb3cgKDwxKSIsDQogICAgYXZnX2xhc3Qyd2Vla3MgPj0gMSAmIGF2Z19sYXN0MndlZWtzIDwgMiB+ICJNb2RlcmF0ZSgxLTIpIiwNCiAgICBhdmdfbGFzdDJ3ZWVrcyA+PSAyIH4gIkhpZ2ggKDIrKSINCiAgKSkNCg0KIyBDcmVhdGUgYSB0YWJsZSBvZiBjb3VudHMgYW5kIGluanVyeSByYXRlcyBieSBncm91cA0KaW5qdXJ5X3JhdGVfYnlfZ3JvdXAgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBncm91cF9ieShsb2FkX2dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX3dlZWtzID0gbigpLA0KICAgIGluanVyeV93ZWVrcyA9IHN1bShpbmp1cnkgPT0gMSksDQogICAgaW5qdXJ5X3JhdGUgPSBpbmp1cnlfd2Vla3MgLyB0b3RhbF93ZWVrcw0KICApICU+JQ0KICBhcnJhbmdlKGxvYWRfZ3JvdXApDQppbmp1cnlfcmF0ZV9ieV9ncm91cA0KDQojIENyZWF0ZSBhIHRhYmxlIG9mIGNvdW50cyBhbmQgaW5qdXJ5IHJhdGVzDQppbmp1cnlfcmF0ZV8yd2Vla2F2ZyA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIGdyb3VwX2J5KGF2ZzJfZ3JvdXApICU+JQ0KICBzdW1tYXJpc2UoDQogICAgdG90YWxfd2Vla3MgPSBuKCksDQogICAgaW5qdXJpZXMgPSBzdW0oaW5qdXJ5ID09IDEpLA0KICAgIGluanVyeV9yYXRlID0gKGluanVyaWVzIC8gdG90YWxfd2Vla3MpICogMTAwDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyhpbmp1cnlfcmF0ZSkpDQppbmp1cnlfcmF0ZV8yd2Vla2F2Zw0KYGBgDQpDdXJyZW50IHdlZWsgY291bnRzOg0KSW5qdXJpZXMgaW5jcmVhc2Ugc2xpZ2h0bHkgZnJvbSAwLTMgY291bnRzIHdpdGggMyBoYXZpbmcgdGhlIGhpZ2hlc3QgaW5qdXJ5IHJhdGUgb2YgMi4wJS4gNCBhbmQgNSBjb3VudHMgaGF2ZSBubyBpbmp1cmllcyBidXQgYSBsb3cgc2FtcGxlIHNpemUuDQoNCkNvdW50cyBvdmVyIGxhc3QgZmV3IHdlZWtzIChHcm91cGVkKToNCkhpZ2ggY291bnRzIGhhZCBubyBpbmp1cmllcw0KTW9kZXJhdGUgY291bnRzIGhhZCB0aGUgaGlnaGVzdCBpbmp1cnkgcmF0ZSAoMS41MiUpDQpMb3cgY291bnRzIGhhZCBhIGluanVyeSByYXRlIG9mIDAuNzQlDQoNCkluanVyeSByYXRlcyBpbmNyZWFzZWQgZnJvbSBsb3cgY291bnRzICg8MSkgaW5qdXJ5IHJhdGUgb2YgMC43NCUgdG8gbW9kZXJhdGUgY291bnRzICgxLTIpIGluanVyeSByYXRlIG9mIDEuNTIlIHRoZW4gZHJvcHBlZCB0byAwJSBmb3IgaGlnaCBjb3VudHMgKDIrKS4gSG93ZXZlciwgaGlnaCBjb3VudHMgaGF2ZSBhIHNtYWxsIHNhbXBsZSBzaXplICg3OCksIHNvIHRoZSByYXRlIG1heSBiZSB1bnN0YWJsZS4NCg0KYGBge3J9DQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBtdXRhdGUod29ya2xvYWRfZ3JvdXAgPSBjYXNlX3doZW4oDQogICAgYXZnMl9ncm91cCA8IDAuNSB+ICJMb3cgKDwwLjUpIiwNCiAgICBhdmcyX2dyb3VwID49IDAuNSB+ICJIaWdoICg+MC41KSIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKSkNCg0KaW5qdXJ5X3JhdGVfZ3JvdXBlZCA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIGdyb3VwX2J5KHdvcmtsb2FkX2dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX3dlZWtzID0gbigpLA0KICAgIGluanVyaWVzID0gc3VtKGluanVyeSA9PSAxLCBuYS5ybSA9IFRSVUUpLA0KICAgIGluanVyeV9yYXRlID0gKGluanVyaWVzIC8gdG90YWxfd2Vla3MpICogMTAwDQogICkNCmluanVyeV9yYXRlX2dyb3VwZWQNCg0KDQpgYGANCg0KQXZlcmFnZSBvdmVyIHRoZSBsYXN0IHRocmVlIHdlZWtzLiBUaGUgaW5qdXJ5IHJhdGUgaXMgaGlnaGVyIHdoZW4gYXRobGV0ZXMgYXZlcmFnZSBvdmVyIDAuNSBvdmVyIDkwIHBlcmNlbnQgbWF4IHNwcmludCBjb3VudHMuIA0KDQpgYGB7cn0NCiMgTWFrZSBjb250aW5nZW5jeSB0YWJsZQ0KY29udGluZ2VuY3kgPC0gbWF0cml4KGMoOCwgNzE2LCA4LCAxMDg0KSwgbnJvdyA9IDIsIGJ5cm93ID0gVFJVRSkNCmNvbG5hbWVzKGNvbnRpbmdlbmN5KSA8LSBjKCJJbmp1cnkiLCAiTm9Jbmp1cnkiKQ0Kcm93bmFtZXMoY29udGluZ2VuY3kpIDwtIGMoIkhpZ2giLCAiTG93IikNCg0KIyBSdW4gdGVzdA0KY2hpc3EudGVzdChjb250aW5nZW5jeSkNCmBgYA0KU2luY2Ugb3VyIHAtdmFsdWUgb2YgMC41Nj4wLjA1LCB0aGVyZSBpcyBubyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gaW5qdXJ5IHJhdGVzIGJldHdlZW4gdGhlIEhpZ2ggYW5kIExvdyA+OTAgY291bnQgZ3JvdXBzLg0KVGhlIGNoaS1zcXVhcmUgdGVzdCBvZiB0aGUgaW5qdXJ5IHJhdGVzIG9mIGF2ZXJhZ2UgPjkwIGNvdW50cyBvZiB0aGUgbGFzdCAzIHdlZWtzIChlaXRoZXIgaGlnaCA+IDAuNSwgb3IgbG93IDwgMC41KSBzaG93ZWQgbm8gc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBpbmp1cnkgcmF0ZXMgYmV0d2VlbiB0aGVzZSBncm91cHMuDQoNCkl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIG51bWJlciBvZiBpbmp1cnkgZXZlbnRzIG9ic2VydmVkIGluIHRoZSBkYXRhc2V0IGlzIHJlbGF0aXZlbHkgbG93LiBBcyBhIHJlc3VsdCwgdGhlIHN0YXRpc3RpY2FsIHBvd2VyIHRvIGRldGVjdCBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiBpbmp1cnkgcmF0ZXMgYmV0d2VlbiBncm91cHMgaXMgbGltaXRlZC4gVGhpcyBsb3cgZXZlbnQgY291bnQgaW5jcmVhc2VzIHRoZSBsaWtlbGlob29kIG9mIGEgVHlwZSBJSSBlcnJvciAoZmFpbGluZyB0byBkZXRlY3QgYSB0cnVlIGVmZmVjdCksIGFuZCB0aHVzLCBub24tc2lnbmlmaWNhbnQgcmVzdWx0cyBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgd2l0aCBjYXV0aW9uLiBGdXR1cmUgc3R1ZGllcyB3aXRoIGxhcmdlciBzYW1wbGUgc2l6ZXMgb3IgbW9yZSBpbmp1cnkgb2NjdXJyZW5jZXMgYXJlIG5lZWRlZCB0byBiZXR0ZXIgYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB3b3JrbG9hZCBhbmQgaW5qdXJ5IHJpc2suDQoNCmBgYHtyfQ0KIyBULXRlc3RzOg0KDQojIEN1cnJlbnQgd2Vlaw0KdC50ZXN0KGNvdW50XzkwcGN0IH4gaW5qdXJ5LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCB2YXIuZXF1YWwgPSBGQUxTRSkNCg0KIyBXZWVrIGJlZm9yZQ0KdC50ZXN0KGxhZzFfOTBwY3RfY291bnQgfiBpbmp1cnksIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsIHZhci5lcXVhbCA9IEZBTFNFKQ0KDQojIENoYW5nZSBsYXN0IHdlZWsgdG8gdGhpcyB3ZWVrDQp0LnRlc3QoY2hhbmdlX2xhc3R3ZWVrIH4gaW5qdXJ5LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCB2YXIuZXF1YWwgPSBGQUxTRSkNCg0KIyBDaGFuZ2UgbGFzdCB0d28gd2Vla3MNCnQudGVzdChjaGFuZ2VfbGFzdDJ3ZWVrcyB+IGluanVyeSwgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgdmFyLmVxdWFsID0gRkFMU0UpDQoNCiMgU3VtIG92ZXIgbGFzdCAyIHdlZWtzDQp0LnRlc3Qoc3VtX2xhc3Qyd2Vla3MgfiBpbmp1cnksIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsIHZhci5lcXVhbCA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgbXV0YXRlKA0KICAgIHNwcmludF9leHBvc3VyZV9iaW4gPSBpZmVsc2UoY291bnRfOTBwY3QgPiAwLCAiU29tZSIsICJOb25lIikNCiAgKQ0KDQp0YWJsZV9leHBvc3VyZSA8LSB0YWJsZSh3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkc3ByaW50X2V4cG9zdXJlX2Jpbiwgd2Vla2x5XzkwcGN0X2luanVyaWVzJGluanVyeSkNCg0KIyBGaXNoZXIncyBFeGFjdCBUZXN0DQpmaXNoZXIudGVzdCh0YWJsZV9leHBvc3VyZSkNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIHRvcCBxdWFydGlsZSBleHBvc3VyZQ0KY3V0b2ZmIDwtIHF1YW50aWxlKHdlZWtseV85MHBjdF9pbmp1cmllcyRjb3VudF85MHBjdCwgMC43NSwgbmEucm0gPSBUUlVFKQ0KDQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBtdXRhdGUoDQogICAgc3ByaW50X2V4cG9zdXJlX2dyb3VwID0gaWZlbHNlKGNvdW50XzkwcGN0ID49IGN1dG9mZiwgIkhpZ2giLCAiTG93IikNCiAgKQ0KDQp0YWJsZV9xdWFydGlsZSA8LSB0YWJsZSh3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkc3ByaW50X2V4cG9zdXJlX2dyb3VwLCB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkaW5qdXJ5KQ0KDQpmaXNoZXIudGVzdCh0YWJsZV9xdWFydGlsZSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2xtKGluanVyeSB+IGNvdW50XzkwcGN0LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KDQpnbG0oaW5qdXJ5IH4gbGFnMV85MHBjdF9jb3VudCwgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KY2hhbmdlIDwtIGdsbShpbmp1cnkgfiBjaGFuZ2VfbGFzdHdlZWssIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQpzdW1tYXJ5KGNoYW5nZSkNCmdsbShpbmp1cnkgfiBjaGFuZ2VfbGFzdDJ3ZWVrcywgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KDQpzdW0gPC0gZ2xtKGluanVyeSB+IHN1bV9sYXN0M3dlZWtzLCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0Kc3VtbWFyeShzdW0pDQpgYGANCg0KYGBge3J9DQptdWx0aV9tb2RlbCA8LSBnbG0oDQogIGluanVyeSB+IGNvdW50XzkwcGN0ICsgc3VtX2xhc3Qyd2Vla3MsDQogIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsDQogIGZhbWlseSA9ICJiaW5vbWlhbCINCikNCg0Kc3VtbWFyeShtdWx0aV9tb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KHdlZWtseV85MHBjdF9pbmp1cmllcywgYWVzKHggPSBjaGFuZ2VfbGFzdHdlZWssIHkgPSBpbmp1cnkpKSArDQogIGdlb21faml0dGVyKGhlaWdodCA9IDAuMDUsIGFscGhhID0gMC4zLCBjb2xvciA9ICJibHVlIikgKyAgIyBKaXR0ZXIgdG8gc3ByZWFkIHBvaW50cyB2ZXJ0aWNhbGx5DQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIHNlID0gVFJVRSwgY29sb3IgPSAicmVkIikgKyAgIyBMb2dpc3RpYyByZWdyZXNzaW9uIGN1cnZlDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJvYmFiaWxpdHkgb2YgSW5qdXJ5IHZzIENoYW5nZSBpbiA+OTAlIFNwcmludCBDb3VudHMgKExhc3QgV2VlaykiLA0KICAgIHggPSAiQ2hhbmdlIGluID45MCUgU3ByaW50IENvdW50cyAoQ3VycmVudCBXZWVrIC0gUHJldmlvdXMgV2VlaykiLA0KICAgIHkgPSAiSW5qdXJ5ICgwID0gTm8sIDEgPSBZZXMpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KTm8gdC10ZXN0cyBvciBtb2RlbHMgcmV2ZWFsZWQgYW55IGluc2lnaHRzLiANCg0KDQoNCg0KDQoNCg0KYGBge3IgcmVtb3ZpbmcgdW5uZWNlc3Nhcnkgb2JqZWN0cyBmb3IgcnVubmluZyBpbWJhbGFuY2UgYW5hbHlzaXN9DQojcmVtb3ZpbmcgZXh0cmEgZGF0YQ0KcmVtb3ZlKGJhbmRfbG9uZywgYmlncywgY2xlYW5fYW5vbnMsIGNvbWJvLCBjb3JfZGF0YSwgY29yX21hdHJpeCwgZGFpbHlfOTBfY291bnRzLA0KICAgICAgIGZ1bGxfZ3JpZCwgaGl0XzkwX2NvdW50cywgSURfMTEsIEluY2lkZW50X2RhdGVzLCBpbmp1cmVkX2RhdGEsIGluanVyaWVzX2FuZF9ydW5uaW5nLA0KICAgICAgIGluanVyeV9ieV9ncm91cCwgaW5qdXJ5X3dlZWtzLCBtYXhfdmVsb2NpdGllcywgbW9kZWxfYWxsX2JhbmRzLCBtb2RlbF9iaWcsDQogICAgICAgbW9kZWxfYmlncywgbW9kZWxfY29tYm8sIG1vZGVsX3NraWxsLCBwbGF5ZXJfY291bnRzX2xvbmcsIHBsYXllcl9kYXRhLA0KICAgICAgIHBsYXllcl9wb3NpdGlvbnMsIHBsb3RfZGF0YSwgcGxvdHNfYnlfcG9zaXRpb24sIHBvc2l0aW9uX2F2ZXJhZ2VzLA0KICAgICAgIHBvc2l0aW9uX2F2ZXJhZ2VzX3dpdGhfdGVhbSwgcHJlX2luanVyeV93ZWVrcywgUUJzLCByZWxhdGl2ZV9iYW5kcywNCiAgICAgICByZWxhdGl2ZV9iYW5kc19sb25nLCBza2lsbCwgc3ByaW50X2NvdW50cywgc3ByaW50X2V4cG9zdXJlX2JpZywNCiAgICAgICBzcHJpbnRfZXhwb3N1cmVfY29tYm8sIHNwcmludF9leHBvc3VyZV9za2lsbCwgc3ByaW50X2luanVyeV90YWJsZSwgVjgsIA0KICAgICAgIHdlZWtseV85MF9jb3VudHMsIHdlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCwgd2Vla2x5X2VmZm9ydCwgd2Vla2x5X2VmZm9ydF9sb25nLA0KICAgICAgIHdlZWtseV9lZmZvcnRfd2lkZSwgd2Vla2x5X21heF9wY3QsIHdlZWtseV9tYXhfdmVsb2NpdHksIHdlZWtseV9zcHJpbnRfY291bnRzLA0KICAgICAgIHdlZWtseV9zcHJpbnRfY291bnRzX2xhZ2dlZCwgd2Vla2x5X3NwcmludF9leHBvc3VyZSwgd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMpDQoNCiNyZW1vdmluZyBleHRyYSB2YWx1ZXMgYW5kIGZ1bmN0aW9ucw0KcmVtb3ZlKGFsbF9hdGhsZXRlcywgYWxsX3dlZWtzLCBiYW5kX2xldmVscywgQklHLCBDT01CTywgU0tJTEwsIGluanVyZWRfaWRzLA0KICAgICAgIGluanVyeV93ZWVrX251bXMsIG92ZXJhbGxfYXZnLCBwbGF5ZXJfaWQsIHBvc2l0aW9ucywgUG9zaXRpb25zLCBxYl9hdmcsIHRlYW1fYXZnLA0KICAgICAgIHBsb3RfaGl0XzkwX2J5X3Bvc2l0aW9uKQ0KYGBgDQoNCg0KIyMgTWF4aW11bSBWZWxvY2l0eSBUcmVuZHMgb3ZlciBUaW1lDQpgYGB7cn0NCmNhdGFwdWx0X2lkcyA8LSB1bmlxdWUoQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiRhbm9uX2lkKQ0KDQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUod2VlayA9IGZsb29yX2RhdGUoRGF0ZSwgdW5pdD0id2VlayIpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgbXV0YXRlKFdlZWsuTWF4LlZlbG9jaXR5ID0gbWVhbihuYS5vbWl0KFNlc3Npb24uTWF4LlZlbG9jaXR5KSkpDQoNCmtlbmRhbGxzIDwtIHJlcChOQSwgMTA0KQ0KbmVnYXRpdmVfdHJlbmQgPC0gIjAiDQoNCmZvcihpIGluIDE6MTA0KXsNCiAgDQogIHNwZWVkcyA8LSB1bmlxdWUoQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbltDYXRhcHVsdF9TZXNzaW9uX2NsZWFuJGFub25faWQ9PWNhdGFwdWx0X2lkc1tpXSxdJFdlZWsuTWF4LlZlbG9jaXR5KQ0KICB3ZWVrcyA8LSByZXYoMTpsZW5ndGgoc3BlZWRzKSkNCiAgDQogIGtlbmQgPC0gY29yKHdlZWtzLCBzcGVlZHMsIG1ldGhvZD0ia2VuZGFsbCIpDQogIA0KICBpZihsZW5ndGgoc3BlZWRzKT41KXsNCiAga2VuZGFsbHNbaV0gPSBrZW5kDQogIA0KICAgIGlmKGtlbmQgPD0gLTAuNSl7DQogICAgICBuZWdhdGl2ZV90cmVuZCA8LSBjKG5lZ2F0aXZlX3RyZW5kLCBjYXRhcHVsdF9pZHNbaV0pDQogICAgfQ0KICANCiAgcCA8LSBnZ3Bsb3QoZmlsdGVyKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4sIGFub25faWQgPT0gY2F0YXB1bHRfaWRzW2ldKSwgYWVzKHdlZWssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXZWVrLk1heC5WZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChjb2xvcj1pZmVsc2Uoa2VuZCA8PSAtMC41LCAicmVkIiwgImJsYWNrIikpICsNCiAgZ2VvbV9saW5lKGNvbG9yPWlmZWxzZShrZW5kIDw9IC0wLjUsICJyZWQiLCAiYmxhY2siKSkgKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgVG9wIFJ1bm5pbmcgU3BlZWQgcGVyIFdlZWsiLCBzdWJ0aXRsZT1rZW5kKSArDQogIHlsaW0oMCwyNSkNCiAgICAgICANCiAgICAgICANCiAgcHJpbnQocCkNCiAgDQogIA0KICB9DQogIA0KfQ0KYGBgDQpgYGB7cn0NCmhpc3Qoa2VuZGFsbHMpDQphYmxpbmUodj0tMC41KQ0KYWJsaW5lKHY9MC41KSANCmFibGluZSh2PW1lZGlhbihuYS5vbWl0KGtlbmRhbGxzKSkpDQpgYGANCkJhc2VkIG9uIHRoZSBLZW5kYWxsIENvcnJlbGF0aW9ucyBjYWxjdWxhdGVkIGZyb20gdGhlIGF2ZXJhZ2UgbWF4aW11bSB2ZWxvY2l0eSBwZXIgd2VlayBmb3IgZWFjaCBwbGF5ZXIgd2UgY2FuIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIGNvcnJlbGF0aW9ucyBzaG93cyB0aGF0IG1vc3QgcGxheWVycyB0ZW5kIHRvIGhhdmUgYSB3ZWFrIEtlbmRhbGwgQ29ycmVsYXRpb24gY29lZmZpY2llbnQuIFRoaXMgc3VnZ2VzdHMgdGhhdCBwbGF5ZXIgbWF5IHRlbmQgdG8gc2VlIGEgZGVjcmVhc2UgaW4gbWF4aW11bSB2ZWxvY2l0eSBwZXIgd2VlayB0aHJvdWdob3V0IHRoZWlyIHRpbWUgYXQgQ1UuIEZvciBtb3N0IHBsYXllcnMgd2l0aCBhIG5lZ2F0aXZlIGNvZWZmaWNpZW50IHRob3VnaCwgaXQgaXMgY29uc2lkZXJlZCByZWFsbHkgd2Vhay4gVGhlIHBsYXllcnMgd2l0aCBhIHN0cm9uZyBuZWdhdGl2ZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBhcmUgSUQgMzAsIElEIDE3OCwgSUQgMjIwLCBhbmQgSUQgNjUuIFRoZXNlIGFyZSB0aGUgb25lcyB0aGF0IGhhdmUgYWxtb3N0IGEgZGVmaW5pdGl2ZSBuZWdhdGl2ZSB0cmVuZCBpbiB0aGVpciBtYXhpbXVtIHJ1bm5pbmcgc3BlZWRzLiBUaGVzZSBwbGF5ZXJzIGFyZSB0aGUgb25seSBvbmVzIHRoYXQgd2UgY2FuIHNheSBmb3IgY2VydGFpbiBoYXZlIHNlZW4gYSBkZWNyZWFzZSBpbiB0aGVpciB0b3AgcnVubmluZyBzcGVlZC4gDQoNCmBgYHtyfQ0KZGF0YV8xMzAgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgPT0gIklEXzEzMCIpDQpkYXRhXzE3OCA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCA9PSAiSURfMTc4IikNCmRhdGFfMjIwIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkID09ICJJRF8yMjAiKQ0KZGF0YV82NSA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCA9PSAiSURfNjUiKQ0KDQptb2RlbF8xMzAgPC0gbG0oV2Vlay5NYXguVmVsb2NpdHkgfiB3ZWVrLCBkYXRhPWRhdGFfMTMwKQ0Kc3VtbWFyeShtb2RlbF8xMzApDQptb2RlbF8xNzggPC0gbG0oV2Vlay5NYXguVmVsb2NpdHkgfiB3ZWVrLCBkYXRhPWRhdGFfMTc4KQ0Kc3VtbWFyeShtb2RlbF8xNzgpDQptb2RlbF8yMjAgPC0gbG0oV2Vlay5NYXguVmVsb2NpdHkgfiB3ZWVrLCBkYXRhPWRhdGFfMjIwKQ0Kc3VtbWFyeShtb2RlbF8yMjApDQptb2RlbF82NSA8LSBsbShXZWVrLk1heC5WZWxvY2l0eSB+IHdlZWssIGRhdGE9ZGF0YV82NSkNCnN1bW1hcnkobW9kZWxfNjUpDQpgYGANCkxvb2tpbmcgYXQgdGhlIHNsb3BlIGNvZWZmaWNpZW50cyBmb3IgcGxheWVycyB0aGF0IGhhZCB0aGUgbG93ZXN0IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyB3ZSBjYW4gc2VlIHRoYXQgYWxsIG9mIHRoZW0gaGFkIGEgcmVsYXRpdmVseSBzbWFsbCBzbG9wZSB2YWx1ZS4gQWxsIG9mIHRoZW0gYmVpbmcgbG93ZXIgdGhhbiAwLjA0IGluIGFic29sdXRlIHZhbHVlLiBUaGlzIHN1Z2dlc3RzIHRoYXQgd2hpbGUgdGhleSBoYWQgYSBkZXRlY3RhYmxlIG5lZ2F0aXZlIHRyZW5kIHRocm91Z2hvdXQgdGhlaXIgdGltZSBhdCBDVSwgbm9uZSBvZiB0aGVtIGhhZCBhbnkgc2hhcnAgZGVjcmVhc2VzLiANCg0KDQojIFNlY3Rpb24gMjogUnVubmluZyBJbWJhbGFuY2UNCg0KIyMgV2hhdCBpcyB0aGUgdmFyaWF0aW9uIGF0IHRoZSB0ZWFtIGxldmVsIGFuZCBhdCBlYWNoIGluZGl2aWR1YWwgYXRobGV0ZSBsZXZlbD8NCg0KICBMb29raW5nIGF0IHRlYW0gZGF0YSBhcyBhIHdob2xlLCBzaW5jZSBKYW51YXJ5IDEsIDIwMjQgdGhlcmUgaXMgYWJzb2x1dGUgbm8gZGV2aWFuY2UgZnJvbSAwLiBUaGF0IG1lYW5zIHRoYXQgc2luY2UgSmFudWFyeSAxLCAyMDI0LCB0aGUgdGVhbSBoYXMgaGFkIHRoZSBzYW1lIGF2ZXJhZ2UgcnVubmluZyBpbWJhbGFuY2Ugb2YgMC4gVGhpcyBtYWtlcyBzZW5zZSBnaXZlbiB0aGF0IHRoZSB0ZWFtIGlzIHNvIGxhcmdlIGFuZCB0aGF0IGltYmFsYW5jZXMgY2FuIGdvIGZyb20gLTEwMCUgdG8gMTAwJS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRocm91Z2hvdXQgdGhpcyB0aW1lLCBhdCBubyBwb2ludCB3YXMgdGhlcmUgYSB0ZWFtIHN3YXkgdG8gb25lIHNpZGUuIFRoZXJlIGFsc28gd2VyZW4ndCBhbnkgcG9pbnRzIHNpbmNlIEphbnVhcnkgMSwgMjAyNCB0aGF0IHRoZSB0ZWFtIGhhZCBhbnkgbGFyZ2Ugc3Bpa2VzIGluIGF2ZXJhZ2UgYWJzb2x1dGUgdmFsdWUgb2YgcnVubmluZyBpbWJhbGFuY2UuIFRoaXMgc3VnZ2VzdHMgdGhhdCBhdCBubyBwb2ludCB0aHJvdWdob3V0IHRoZSBzZWFzb24gd2VyZSB0aGVyZSBsYXJnZXIgc3Bpa2VzIHRoYW4gbm9ybWFsIGluIHJ1bm5pbmcgaW1iYWxhbmNlLiANCiAgRWFjaCBwbGF5ZXIgdGVuZHMgdG8gaGF2ZSBhIHZlcnkgdW5pcXVlIHRyZW5kIGluIHRoZWlyIHJ1bm5pbmcgaW1iYWxhbmNlLiBMb29raW5nIGF0IGhvdyB0aGUgdGVhbSB2YXJpZXMgYnV0IGFsc28gYXQgaG93IGVhY2ggcGxheWVyIHZhcmllcyB0aHJvdWdob3V0IHRoZSBzZWFzb24sIGl0J3MgaGFyZCB0byBtYWtlIG91dCBhbnkgcGF0dGVybiB0aGF0J3MgYXBwbGljYWJsZSB0byBtb3N0IHBlb3BsZS4gVGhlIHZhcmlhbmNlIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIHZhcmllcyBncmVhdGx5IGJldHdlZW4gZWFjaCBwbGF5ZXIuIEluc3RlYWQgd2UgbG9va2VkIGF0IHRoZSB2YXJpYW5jZXMgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdC4gQmFzZWQgb24gdGhyZWUgZGlmZmVyZW50IGJvb3RzdHJhcHBlZCBmaW5kaW5ncywgd2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW5jZXMgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdCB3ZXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuDQogIA0KICBGb3IgdGhlIGZpcnN0IGJvb3RzdHJhcCwgd2UgY29tcGFyZWQgdGhlIHZhcmlhbmNlIG9mIHRoZSBwb29sZWQgZ3JvdXBzIG1lYW5pbmcgdGhhdCB0aGUgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgZm9yIHBsYXllcnMgd2hvIHdlcmUgaW5qdXJlZCBhbmQgdGhvc2Ugd2hvIHdlcmUgbm90IHdlcmUgY29tcGFyZWQuIFRoaXMgcmVzdWx0ZWQgaW4gYSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCB3aGljaCBzdWdnZXN0ZWQgdGhhdCB0aGUgZGlmZmVyZW5jZSBpbiB2YXJpYW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIGlzIGJldHdlZW4gMC4zMCBhbmQgMy40NS4gVGhpcyBzdWdnZXN0cyB0aGF0IHdoZW4gbG9va2luZyBhdCB0aGUgdmFyaWFuY2Ugb2YgdGhlIHR3byBncm91cHMgc2VwYXJhdGVseSBidXQgYWxsIHRoZSBwbGF5ZXJzIGFyZSBwb29sZWQgdG9nZXRoZXIsIHRoZSB2YXJpYW5jZXMgd2lsbCBtb3N0IGxpa2VseSBiZSBkaWZmZXJlbnQgYnkgZmFjdG9yIGJldHdlZW4gMC4zMCBhbmQgMy40NSBhbmQgdGhlIHZhcmlhbmNlIGZvciB0aGUgaW5qdXJlZCBwb29sIHdpbGwgYmUgZ3JlYXRlciB0aGFuIHRoYXQgb2YgdGhlIHVuaW5qdXJlZCBwb29sLg0KICBGb3IgdGhlIHNlY29uZCBib290c3RyYXAsIGVhY2ggcGxheWVyJ3MgdmFyaWFuY2Ugd2FzIHRha2VuIGluZGl2aWR1YWxseS4gVGhpcyB1bnBvb2xlZCBhcHByb2FjaCB3YXMgdGFrZW4gdG8gc2VlIGlmIGFuIGluZGl2aWR1YWwgcGxheWVyJ3MgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgY291bGQgcG90ZW50aWFsbHkgYmUgcmVsYXRlZCB0byBIU0kgcmlzay4gVGhlIGJvb3RzdHJhcCBhbGdvcml0aG0gaW4gdGhpcyBjYXNlIHRvb2sgdGhlIGF2ZXJhZ2VkIHZhcmlhbmNlcyBvZiB0aGUgYm9vdHN0cmFwcGVkIHNhbXBsZSBmb3IgZWFjaCBncm91cCBhbmQgY29tcGFyZWQgdGhlbS4gVGhpcyBib290c3RyYXAgcHJvZHVjZWQgYSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSB2YXJpYW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIGlzIDAuNzkgdG8gMS4zMi4gVGhlc2UgcmVzdWx0cyBzdWdnZXN0IHRoYXQgcGxheWVycyB3aG8gc3VzdGFpbmVkIGEgaGFtc3RyaW5nIGluanVyeSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQgaGFkLCBvbiBhdmVyYWdlLCBhIGdyZWF0ZXIgdmFyaWFuY2UgaW4gdGhlaXIgcnVubmluZyBpbWJhbGFuY2UgYnkgYWJvdXQgMS4wNi4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgYW5kIEhTSSByaXNrLiBUaGlzIGZvdW5kIGluY3JlYXNlIGluIHZhcmlhYmlsaXR5IHdpbGwgYmUgdXNlZCB0byBhZGRyZXNzIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLg0KICBGb3IgdGhlIHRoaXJkIGJvb3RzdHJhcCwgd2Ugd2FudGVkIHRvIHNlZSBpZiB0aGVyZSB3YXMgYSBkaWZmZXJlbmNlIGluIHRoZSBhdmVyYWdlIG1lYW4gYWJzb2x1dGUgdmFsdWUgaW4gcnVubmluZyBpbWJhbGFuY2UgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGFuZCB3ZXJlbid0IGluanVyZWQuIFRoZSBib290c3RyYXBwaW5nIGFsZ29yaXRobSBmb3IgdGhpcyB0ZXN0IGNhbGN1bGF0ZWQgdGhlIGF2ZXJhZ2UgYWJzb2x1dGUgZGlzdGFuY2UgdmFsdWUgb3IgZWFjaCBydW5uaW5nIGltYmFsYW5jZSBtZWFzdXJlbWVudCBhbmQgZm91bmQgdGhlIGF2ZXJhZ2UgZm9yIGVhY2ggc2FtcGxlLiBUaGlzIHRlc3QgZm91bmQgdGhhdCBhdCB0aGUgOTAlIHNpZ25pZmljYW5jZSBsZXZlbCwgaW5qdXJlZCBwbGF5ZXJzIGhhZCBhbiBhdmVyYWdlIHJ1bm5pbmcgaW1iYWxhbmNlIGFic29sdXRlIHZhbHVlIGJldHdlZW4gMC4wNiBhbmQgMC4zMiBncmVhdGVyIHRoYW4gdGhlaXIgdW5pbmp1cmVkIGNvdW50ZXJwYXJ0cy4gVGhlc2UgdmFsdWVzIHRob3VnaCwgd2hlbiB3ZSBjb25zaWRlciB0aGF0IHRoZSByYW5nZSBvZiBydW5uaW5nIGltYmFsYW5jZSBnb2VzIGZyb20gMCB0byAxMDAgaXMgc21hbGwgYW5kIG1heSBiZSBoYXJkIHRvIGRldGVjdCB3aGVuIG91dCBpbiB0aGUgZmllbGQuIA0KICANCiAgV2UgYWxzbyBsb29rZWQgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJ1bm5pbmcgaW1iYWxhbmNlIGFuZCBoaWdoZXIgbGV2ZWwgcG9zaXRpb24uIEZyb20gdGhpcyBhbmFseXNpcyB3ZSBmb3VuZCB0aGF0IHRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhIHN1cGVyIHN0cm9uZyByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdGhyZWUgY2F0ZWdvcmllcyBhbmQgYXZlcmFnZSBydW5uaW5nIGltYmFsYW5jZSB2YXJpYW5jZS4gVGhlIGJvb3RzdHJhcCByZXZlYWxlZCB0aGF0IHRoZXJlIGFyZSBwb3RlbnRpYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRob3NlIHdobyBhcmUgQmlncyBhbmQgQ29tYm9zLiBCdXQsIHRob3NlIHdobyBhcmUgU2tpbGxzIHdlcmVuJ3QgYWJsZSB0byBkaWZmZXJlbnRpYXRlIHRoZW1zZWx2ZXMgYmV0d2VlbiB0aGUgdHdvIGdyb3Vwcy4gDQogIEFsb25nIHdpdGggdGhpcywgd2UgbG9va2VkIGF0IHRoZSBhdmVyYWdlIGFic29sdXRlIHZhbHVlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciB0aGUgdGhyZWUgZ3JvdXBzLiBUaGlzIGFuYWx5c2lzIHRvbGQgdXMgdGhhdCB3aGlsZSBDb21ib3MgYW5kIEJpZ3MgdGVuZGVkIHRvIGhhdmUgdGhlIHNhbWUgYXZlcmFnZSBhYnNvbHV0ZSB2YWx1ZSBydW5uaW5nIGltYmFsYW5jZSwgU2tpbGxzIGhhZCBhIHNpZ25pZmljYW50bHkgaGlnaGVyIGF2ZXJhZ2UgYWJzb2x1dGUgcnVubmluZyBpbWJhbGFuY2UgdmFsdWUuIA0KICBXZSBjYW4gc2VlIGZyb20gdGhlIHZlcnkgbGFzdCBjaGFydCBpbiB0aGlzIGFuYWx5c2lzIHRoYXQgU2tpbGxzIG1ha2UgdXAgdGhlIG1vc3Qgb2YgdGhvc2Ugd2l0aCBoYW1zdHJpbmcgaW5qdXJpZXMgZm9sbG93ZWQgY2xvc2VseSBieSBDb21ib3MgYW5kIEJpZ3MgbWFraW5nIHVwIGFyb3VuZCBoYWxmIG9mIHRoZSBhbW91bnQgb2YgU2tpbGxzLiBUaGlzIGlzIGludGVyZXN0aW5nIGNvbnNpZGVyaW5nIHRoYXQgdGhlIGFtb3VudHMgb2YgQmlncywgU2tpbGxzLCBhbmQgQ29tYm9zIHdpdGhpbiB0aGUgSGlzdG9yaWNhbCBSdW5uaW5nIGRhdGEgc2V0IGFyZSBhbGwgcm91Z2hseSB0aGUgc2FtZS4gDQoNCiMjIyBUZWFtIEFuYWx5c2lzDQpgYGB7ciBMb29raW5nIGF0IHRlYW0gYW5kIHBsYXllciB2YXJpYW5jZXMgb2YgcnVubmluZyBpbWJhbGFuY2V9DQojdGVhbSB2YXJpYXRpb24NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgc3VtbWFyaXplKFRlYW1fVmFyaWF0aW9uID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSkNCg0KI2luZGl2aWR1YWwgcGxheWVyIHZhcmlhdGlvbg0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgc3VtbWFyaXplKFBsYXllcl9WYXJpYXRpb24gPSB2YXIobmEub21pdChSdW5uaW5nLkltYmFsYW5jZSkpKSAlPiUNCiAgdW5ncm91cCgpIA0KDQojYXZlcmFnZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBhY3Jvc3MgYWxsIHBsYXllcnMNCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIudmFyID0gdmFyKG5hLm9taXQoUnVubmluZy5JbWJhbGFuY2UpKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgc3VtbWFyaXplKEF2ZXJhZ2VfUGxheWVyX1ZhcmlhbmNlID0gbWVhbihuYS5vbWl0KFBsYXllci52YXIpKSkNCg0KI21ha2luZyB2YXJpYW5jZSBhbmQgYXZlcmFnZSBhYnNvbHV0ZSB2YWx1ZSBmb3IgZWFjaCBkYXRlIHRvIHNlZSB0cmVuZHMNCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGdyb3VwX2J5KERhdGUpICU+JQ0KICBtdXRhdGUoRGF0ZS5WYXJpYW5jZSA9IHZhcihuYS5vbWl0KFJ1bm5pbmcuSW1iYWxhbmNlKSksDQogICAgICAgICBEYXRlLkF2Zy5BYnMuVmFsdWUgPSBtZWFuKGFicyhuYS5vbWl0KFJ1bm5pbmcuSW1iYWxhbmNlKSkpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBSdW5uaW5nIGltYmFsYW5jZSBtZWFzdXJlbWVudHMgZm9yIHdob2xlIHRlYW0gc2luY2UgSmFudWFyeSAxLCAyMDI0fQ0KI2NhbGN1bGF0aW5nIG1lYW4gYW5kIHZhcmlhbmNlIGZvciB0ZWFtIGRhdGENCnRlYW1fbWVhbiA8LSBtZWFuKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiRSdW5uaW5nLkltYmFsYW5jZSkNCnRlYW1fc2QgPC0gc2QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJFJ1bm5pbmcuSW1iYWxhbmNlKQ0KDQojbWFraW5nIHNjYXR0ZXIgcGxvdCBvZiB0ZWFtIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgdGhyb3VnaG91dCBzZWFzb24NCmdncGxvdChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4sIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiArIHRlYW1fc2QpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuIC0gdGVhbV9zZCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4gKyAoMip0ZWFtX3NkKSwgY29sb3IgPSAiI0EyQTRBMyIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuIC0gKDIqdGVhbV9zZCksIGNvbG9yID0gIiNBMkE0QTMiKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJUZWFtIFJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCIsIHk9IlJ1bm5pbmcgSW1iYWxhbmNlICglKSIsDQogICAgICAgc3VidGl0bGUgPSAiXHUwM0JDID0gMC4wODYyMzQxMiwgXHUwM0MzXjIgPSAxNC45NDIxNSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNtYWtpbmcgc2NhdHRlciBwbG90IG9mIHRlYW0gcnVubmluZyBpbWJhbGFuY2UgZGF0YSB0aHJvdWdob3V0IHNlYXNvbg0KZ2dwbG90KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX2xpbmUoYWVzKHg9RGF0ZSwgeT1EYXRlLkF2Zy5BYnMuVmFsdWUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX2xpbmUoYWVzKHg9RGF0ZSwgeT0tRGF0ZS5BdmcuQWJzLlZhbHVlKSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJUZWFtIFJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCIsIHk9IlJ1bm5pbmcgSW1iYWxhbmNlICglKSIsDQogICAgICAgc3VidGl0bGUgPSAiXHUwM0JDID0gMC4wODYyMzQxMiwgXHUwM0MzXjIgPSAxNC45NDIxNSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNtYWtpbmcgaGlzdG9ncmFtIG9mIHRlYW0gcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KZ2dwbG90KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgbGFicyh0aXRsZSA9ICJUZWFtIFJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCIsIHg9IlJ1bm5pbmcgSW1iYWxhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMjIyBJbmp1cmVkIEFuYWx5c2lzDQpgYGB7ciBMb29raW5nIGF0IGRpc3RyaWJ1dGlvbnMgZm9yIGluanVyZWQgYW5kIHVuaW5qdXJlZCBzZXBhcmF0ZWx5fQ0KI21ha2luZyBoaXN0b2dyYW0gZm9yIHJ1bm5pbmcgaW1iYWxhbmNlIG9mIGFsbCBpbmp1cmVkIGF0aGxldGVzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiI0NGQjg3QyIsIGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBjb25maWRlbmNlIGludGVydmFsDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDI1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NzUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICB4bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGggSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNtYWtpbmcgaGlzdG9ncmFtIGZvciBydW5uaW5nIGltYmFsYW5jZSBvZiBhbGwgdW5pbmp1cmVkIGF0aGxldGVzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJibGFjayIsIGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBjb25maWRlbmNlIGludGVydmFsDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC4wMjUpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NzUpKSArDQogIHhsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aG91dCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KI1Bsb3R0aW5nIGluanVyZWQgYW5kIHVuaW5qdXJlZCBoaXN0b2dyYW1zIG92ZXIgdG9wIG9uZSBhbm90aGVyDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWxwaGEgPSAwLjc1KSArDQogICNhZGRpbmcgaW4gOTUlIENJIGZvciB1bmluanVyZWQgcGxheWVycw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDUpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSksIGZpbGwgPSAiI0NGQjg3QyIsIGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBDSSBmb3IgaW5qdXJlZCBwbGF5ZXJzDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjk1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgeGxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRoIGFuZCB3aXRob3V0IEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3IgbG9va2luZyBhdCB0cmVuZHMgZm9yIGluanVyZWQgYW5kIHVuaW5qdXJlZCBwbGF5ZXJzIHNpbmNlIDEtMS0yMDI0fQ0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgcnVubmluZyBpbWJhbGFuY2UgZGF0YSBmb3IgaW5qdXJlZCBwbGF5ZXJzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArIA0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIHlsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgcnVubmluZyBpbWJhbGFuY2UgZm9yIHVuaW5qdXJlZCBwbGF5ZXJzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgeWxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRob3V0IEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KIyMjIyBCb290c3RyYXBwaW5nIERpZmZlcmVuY2VzIGJldHdlZW4gSW5qdXJlZCBhbmQgVW5pbmp1cmVkIEF0aGxldGVzDQpgYGB7ciBTZXBhcmF0aW5nIGluanVyZWQgYW5kIHVuaW5qdXJlZCBpbnRvIHR3byBkaWZmZXJlbnQgZGF0YSBzZXRzfQ0KI1NwbGl0dGluZyB1cCB0aGUgZGF0YSBzZXRzIGFuZCBjYWxjdWxhdGluZyBwbGF5ZXIgdmFyaWFuY2UgYW5kIG1lYXN1cmVtZW50IGFic29sdXRlIHZhbHVlDQppbmp1cmVkX2RhdGEgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSAlPiUNCiAgbXV0YXRlKFBsYXllci5BYnNvbHV0ZS5EaXN0ID0gYWJzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoUGxheWVyLlZhcmlhbmNlID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQp1bmluanVyZWRfZGF0YSA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0gJT4lDQogIG11dGF0ZShQbGF5ZXIuQWJzb2x1dGUuRGlzdCA9IGFicyhSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKFBsYXllci5WYXJpYW5jZSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQoNCmBgYHtyIEJvb3N0cmFwcGluZyB0byBsb29rIGF0IHZhcmlhbmNlcyBvZiBkaWZmZXJlbnQgZ3JvdXBzLCBwb29sZWR9DQojbWFraW5nIGEgZGF0YSBmcmFtZSB0byBob2xkIGFsbCBvZiB0aGUgd2l0aGluIGdyb3VwIHZhcmlhbmNlcw0KZ3JvdXBfdmFyaWFuY2VzIDwtIGRhdGEuZnJhbWUoaW5qdXJlZF92YXIgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgdW5pbmp1cmVkX3ZhciA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICBkaWZmX2luX3ZhciA9IHJlcChOQSwgNTAwMCkpDQoNCiNib290c3RyYXAgZm9yIHZhcmlhbmNlLCA1MDAwIGl0ZXJhdGlvbnMNCmZvcihpIGluIDE6NTAwMCl7DQogICNyYW5kb20gc2VlZA0KICBzZXQuc2VlZChpKSANCiAgDQogICN0YWtpbmcgc2FtcGxlcyBmcm9tIGVhY2ggb2YgdGhlIGRhdGEgc2V0cywgc2FtZSBudW1iZXIgb2Ygcm93cywgcmVwbGFjZW1lbnQgdHJ1ZQ0KICBpbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbihpbmp1cmVkX2RhdGEsIHJlcGxhY2UgPSBUUlVFLCBzaXplID0gMTY3MikNCiAgdW5pbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbih1bmluanVyZWRfZGF0YSwgcmVwbGFjZT1UUlVFLCBzaXplID0gMjM5MSkNCiAgDQogICNzdG9yaW5nIHRoZSBjYWxjdWxhdGVkIHZhcmlhbmNlcyBpbiBkYXRhIGZyYW1lDQogIGdyb3VwX3ZhcmlhbmNlc1tpLDFdID0gdmFyKGluanVyZWRfc2FtcGxlJFJ1bm5pbmcuSW1iYWxhbmNlKQ0KICBncm91cF92YXJpYW5jZXNbaSwyXSA9IHZhcih1bmluanVyZWRfc2FtcGxlJFJ1bm5pbmcuSW1iYWxhbmNlKQ0KICBncm91cF92YXJpYW5jZXNbaSwzXSA9IGdyb3VwX3ZhcmlhbmNlc1tpLDFdIC0gZ3JvdXBfdmFyaWFuY2VzW2ksMl0NCn0NCmBgYA0KDQoNCmBgYHtyIFBsb3R0aW5nIHJlc3VsdHMgZnJvbSBwb29sZWQgYm9vc3RyYXBwZWQgdmFyaWFuY2VzfQ0KZ2dwbG90KGRhdGE9Z3JvdXBfdmFyaWFuY2VzLCBhZXMoZGlmZl9pbl92YXIpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF92YXJpYW5jZXMkZGlmZl9pbl92YXIsIDAuMDUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC45NSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgbGFicyh4PSJEaWZmZXJlbmNlIGluIFZhcmlhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90KGRhdGE9Z3JvdXBfdmFyaWFuY2VzKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhpbmp1cmVkX3ZhciksIGFscGhhID0gMC43NSwgZmlsbCA9IiNDRkI4N0MiKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh1bmluanVyZWRfdmFyKSwgYWxwaGEgPSAwLjc1KSArDQogIGxhYnMoeD0iVmFyaWFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3IgQm9vdHN0cmFwcGluZyB0byBsb29rIGF0IGF2ZXJhZ2UgcGxheWVyIHZhcmlhbmNlc30NCiNtYWtpbmcgYSBkYXRhIGZyYW1lIHRvIGhvbGQgYWxsIG9mIHRoZSBhdmVyYWdlIHBsYXllciB2YXJpYW5jZXMgYmV0d2VlbiBncm91cHMNCm1lYW5fcGxheWVyX3ZhcmlhbmNlcyA8LSBkYXRhLmZyYW1lKGluanVyZWRfdmFyID0gcmVwKE5BLDUwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF92YXIgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl92YXIgPSByZXAoTkEsIDUwMDApKQ0KDQpmb3IoaSBpbiAxOjUwMDApew0KICAjcmFuZG9tIHNlZWQNCiAgc2V0LnNlZWQoaSkgDQogIA0KICAjdGFraW5nIHNhbXBsZXMgZnJvbSBlYWNoIG9mIHRoZSBkYXRhIHNldHMsIHNhbWUgbnVtYmVyIG9mIHJvd3MsIHJlcGxhY2VtZW50IHRydWUNCiAgaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24oaW5qdXJlZF9kYXRhLCByZXBsYWNlID0gVFJVRSwgc2l6ZSA9IDE2NzIpDQogIHVuaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24odW5pbmp1cmVkX2RhdGEsIHJlcGxhY2U9VFJVRSwgc2l6ZSA9IDIzOTEpDQogIA0KICAjc3RvcmluZyB0aGUgY2FsY3VsYXRlZCB2YXJpYW5jZXMgaW4gZGF0YSBmcmFtZQ0KICBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwxXSA9IG1lYW4obmEub21pdChpbmp1cmVkX3NhbXBsZSRQbGF5ZXIuVmFyaWFuY2UpKQ0KICBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwyXSA9IG1lYW4obmEub21pdCh1bmluanVyZWRfc2FtcGxlJFBsYXllci5WYXJpYW5jZSkpDQogIG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDNdID0gbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksMV0gLSBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIGF2ZXJhZ2VkIGJvb3RzdHJhcHBlZCBwbGF5ZXIgdmFyaWFuY2VzfQ0KZ2dwbG90KGRhdGEgPSBtZWFuX3BsYXllcl92YXJpYW5jZXMsIGFlcyhkaWZmX2luX3ZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKG1lYW5fcGxheWVyX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUobWVhbl9wbGF5ZXJfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHRpdGxlID0gIkRpZmZlcmVuY2UgaW4gRXN0aW1hdGVkIEF2ZXJhZ2UgVmFyaWFuY2UgZm9yIEluanVyZWQgYW5kIFVuaW5qdXJlZCBBdGhsZXRlcyIsIHg9IkRpZmZlcmVuY2UgaW4gQXZlcmFnZSBWYXJpYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChkYXRhPW1lYW5fcGxheWVyX3ZhcmlhbmNlcykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXModW5pbmp1cmVkX3ZhciksIGFscGhhID0gMC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkVzdGltYXRlZCBBdmVyYWdlIFZhcmlhbmNlIGZvciBJbmp1cmVkIGFuZCBVbmluanVyZWQgQXRobGV0ZXMiLCB4PSJBdmVyYWdlIFZhcmlhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIEJvb3RzdHJhcHBpbmcgdG8gbG9vayBhdCBhdmVyYWdlIGFic29sdXRlIHZhbHVlIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIGF2ZXJhZ2UgYWJzb2x1dGUgZGlmZmVyZW5jZXMgZnJvbSAwDQpncm91cF9kaXN0YW5jZSA8LSBkYXRhLmZyYW1lKGluanVyZWRfZGlzdCA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICB1bmluanVyZWRfZGlzdCA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICBkaWZmX2luX2Rpc3QgPSByZXAoTkEsIDUwMDApKQ0KDQojYm9vdHN0cmFwIGZvciB2YXJpYW5jZXMsIDUwMDAgaXRlcmF0aW9ucw0KZm9yKGkgaW4gMTo1MDAwKXsNCiAgI3JhbmRvbSBzZWVkDQogIHNldC5zZWVkKGkpIA0KICANCiAgI3Rha2luZyBzYW1wbGVzIGZyb20gZWFjaCBvZiB0aGUgZGF0YSBzZXRzLCBzYW1lIG51bWJlciBvZiByb3dzLCByZXBsYWNlbWVudCB0cnVlDQogIGluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKGluanVyZWRfZGF0YSwgcmVwbGFjZSA9IFRSVUUsIHNpemUgPSAxNjU4KQ0KICB1bmluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF9kYXRhLCByZXBsYWNlPVRSVUUsIHNpemUgPSAyNDA1KQ0KICANCiAgI3N0b3JpbmcgdGhlIGNhbGN1bGF0ZWQgdmFyaWFuY2VzIGluIGRhdGEgZnJhbWUNCiAgZ3JvdXBfZGlzdGFuY2VbaSwxXSA9IG1lYW4oaW5qdXJlZF9zYW1wbGUkUGxheWVyLkFic29sdXRlLkRpc3QpDQogIGdyb3VwX2Rpc3RhbmNlW2ksMl0gPSBtZWFuKHVuaW5qdXJlZF9zYW1wbGUkUGxheWVyLkFic29sdXRlLkRpc3QpDQogIGdyb3VwX2Rpc3RhbmNlW2ksM10gPSBncm91cF9kaXN0YW5jZVtpLDFdIC0gZ3JvdXBfZGlzdGFuY2VbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIGF2ZXJhZ2VkIGJvb3N0cmFwcGVkIGFic29sdXRlIHZhbHVlIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KZ2dwbG90KGRhdGEgPSBncm91cF9kaXN0YW5jZSwgYWVzKGRpZmZfaW5fZGlzdCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2Rpc3RhbmNlJGRpZmZfaW5fZGlzdCwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfZGlzdGFuY2UkZGlmZl9pbl9kaXN0LCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHRpdGxlID0gIkVzdGltYXRlZCBEaWZmZXJlbmNlIGluIEF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUiLCB4PSJEaWZmZXJlbmNlIGluIEF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoZGF0YSA9IGdyb3VwX2Rpc3RhbmNlKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhpbmp1cmVkX2Rpc3QpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXModW5pbmp1cmVkX2Rpc3QpLCBhbHBoYSA9IDAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgQXZlcmFnZSBBYnNvbHV0ZSBWYWx1ZSIsIHg9IkF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUiKSAgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIFJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZnJvbSBsb29wc30NCiNyZW1vdmluZyBqdW5rIHRoYXQgY2FtZSBmcm9tIHRoZSBsb29wcw0KcmVtb3ZlKGksdGVhbV9tZWFuLCB0ZWFtX3NkLCBpbmp1cmVkX3NhbXBsZSwgdW5pbmp1cmVkX3NhbXBsZSwgZ3JvdXBfdmFyaWFuY2VzLCBpbmp1cmVkX2RhdGEsIHVuaW5qdXJlZF9kYXRhLCBtZWFuX3BsYXllcl92YXJpYW5jZXMsIGdyb3VwX2Rpc3RhbmNlKQ0KYGBgDQoNCg0KIyMjIFBvc2l0aW9uIEFuYWx5c2lzDQpgYGB7ciBzb3J0aW5nIHBsYXllciBpbnRvIGdlbmVyYWwgcG9zaXRpb25zfQ0KI21ha2luZyBsaXN0cyB0byBzb3J0IHBvc2l0aW9uIGludG8gbGFyZ2VyIGNhdGVnb3JpZXMNCkNPTUJPIDwtIGMoIlFCIiwiTEIiLCJURSIsIlJCIiwgIklMQiIpDQpCSUcgPC0gYygiT0wiLCAiREwiLCAiREUiLCAiRFQiKQ0KU0tJTEwgPC0gYygiV1IiLCAiREIiLCAiQ0IiLCAiU0FGIikNClBvc2l0aW9ucyA8LSBjKCJDT01CTyIsICJCSUciLCAiU0tJTEwiKQ0KDQojZ2l2aW5nIHBvc2l0aW9ucyB0byBpbmNpZGVudCByZXBvcnQNCkluY2lkZW50X1JlcG9ydF9jbGVhbiA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW4gJT4lDQogIG11dGF0ZShTcGVjaWZpYy5Qb3NpdGlvbiA9IFBvc2l0aW9uLA0KICAgICAgICAgUG9zaXRpb24gPSBjYXNlX3doZW4oU3BlY2lmaWMuUG9zaXRpb24gJWluJSBDT01CTyB+ICJDT01CTyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BlY2lmaWMuUG9zaXRpb24gJWluJSBCSUcgfiAiQklHIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcGVjaWZpYy5Qb3NpdGlvbiAlaW4lIFNLSUxMIH4gIlNLSUxMIikpDQoNCiNnaXZpbmcgcG9zaXRpb25zIHRvIGNhdGFwdWx0IHNlc3Npb24NCkNhdGFwdWx0X1Nlc3Npb25fY2xlYW4gPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgbXV0YXRlKFNwZWNpZmljLlBvc2l0aW9uID0gUHJpbWFyeS5Qb3NpdGlvbiwNCiAgICAgICAgIFBvc2l0aW9uID0gY2FzZV93aGVuKFByaW1hcnkuUG9zaXRpb24gJWluJSBDT01CTyB+ICJDT01CTyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIEJJRyB+ICJCSUciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBTS0lMTCB+ICJTS0lMTCIpKQ0KDQojb25seSB0YWtpbmcgSURzIGFuZCBwb3NpdGlvbiBuYW1lcyBhbmQgY2F0ZWdvcmllcw0KaW5jaWRlbnRfaW5mbyA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW5bLGMoImFub25faWQiLCAiUG9zaXRpb24iLCAiU3BlY2lmaWMuUG9zaXRpb24iKV0NCmNhdGFwdWx0X2luZm8gPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhblssYygiYW5vbl9pZCIsICJQb3NpdGlvbiIsICJTcGVjaWZpYy5Qb3NpdGlvbiIpXQ0KDQojY29tcHJlaGVuc2l2ZSBsaXN0IG9mIElEcywgdGhlaXIgcG9zaXRpb24sIGFuZCBjYXRlZ29yeQ0KaW5mbyA8LSBkaXN0aW5jdChyYmluZChpbmNpZGVudF9pbmZvLCBjYXRhcHVsdF9pbmZvKSkNCg0KI2FkZCB0aGlzIG9ubHkgaGlzdG9yaWNhbCBydW5uaW5nDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gbGVmdF9qb2luKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgaW5mbywgYnk9ImFub25faWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA9Im1hbnktdG8tbWFueSIpICU+JQ0KICAjY2FsY3VsYXRpbmcgZWFjaCBwbGF5ZXIncyB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKFBsYXllci5WYXJpYW5jZSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQoNCmBgYHtyIExvb2tpbmcgYXQgUnVubmluZyBJbWJhbGFuY2UgfQ0KZm9yKGkgaW4gMTozKXsNCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJFBvc2l0aW9uPT1Qb3NpdGlvbnNbaV0sXSxhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oKSArDQogICAgbGFicyhzdWJ0aXRsZT1Qb3NpdGlvbnNbaV0pDQogIA0KICBwcmludChwKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KUGxheWVyX1N1bW1hcnlfU3RhdHMgPC0gZGlzdGluY3QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuWyxjKCJhbm9uX2lkIiwgIlBvc2l0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3BlY2lmaWMuUG9zaXRpb24iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGxheWVyLlZhcmlhbmNlIildKQ0KYGBgDQoNCg0KYGBge3J9DQpmb3IoaSBpbiAxOjMpew0KICBwIDwtIGdncGxvdChkYXRhPVBsYXllcl9TdW1tYXJ5X1N0YXRzW1BsYXllcl9TdW1tYXJ5X1N0YXRzJFBvc2l0aW9uPT1Qb3NpdGlvbnNbaV0sXSwNCiAgICAgICAgICAgICAgYWVzKFBsYXllci5WYXJpYW5jZSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgICBsYWJzKHN1YnRpdGxlPVBvc2l0aW9uc1tpXSkNCiAgDQogIHByaW50KHApDQp9DQpgYGANCg0KDQojIyMjIEJvb3RzdHJhcHBpbmcgRGlmZmVyZW50IFBvc2l0aW9ucw0KYGBge3J9DQojc3BsaXR0aW5nIHVwIGRhdGEgc2V0IGludG8gdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzDQpDT01CT1MgPC0gUGxheWVyX1N1bW1hcnlfU3RhdHMgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQ09NQk8iKSAlPiUNCiAgbmEub21pdCgpDQoNClNLSUxMUyA8LSBQbGF5ZXJfU3VtbWFyeV9TdGF0cyAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uID09ICJTS0lMTCIpICU+JQ0KICBuYS5vbWl0KCkNCg0KQklHUyA8LSBQbGF5ZXJfU3VtbWFyeV9TdGF0cyAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uID09ICJCSUciKSAlPiUNCiAgbmEub21pdCgpDQoNCmdyb3VwX2F2Z192YXJpYW5jZSA8LSBkYXRhLmZyYW1lKENPTUJPX3ZhciA9IHJlcChOQSwgNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTS0lMTF92YXIgPSByZXAoTkEsIDUwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQklHX3ZhciA9IHJlcChOQSwgNTAwMCkpDQpgYGANCg0KDQpgYGB7cn0NCmZvcihpIGluIDE6NTAwMCl7DQogIHNldC5zZWVkKGkpDQogIGNvbWJvX3NhbXBsZSA8LSBzYW1wbGVfbihDT01CT1MsIHNpemU9MjAsIHJlcGxhY2U9VFJVRSkNCiAgc2tpbGxfc2FtcGxlIDwtIHNhbXBsZV9uKFNLSUxMUywgc2l6ZT0yMiwgcmVwbGFjZT1UUlVFKQ0KICBiaWdfc2FtcGxlIDwtIHNhbXBsZV9uKEJJR1MsIHNpemU9MjQsIHJlcGxhY2U9VFJVRSkNCiAgDQogIGdyb3VwX2F2Z192YXJpYW5jZVtpLDFdIDwtIG1lYW4obmEub21pdChjb21ib19zYW1wbGUkUGxheWVyLlZhcmlhbmNlKSkNCiAgZ3JvdXBfYXZnX3ZhcmlhbmNlW2ksMl0gPC0gbWVhbihuYS5vbWl0KHNraWxsX3NhbXBsZSRQbGF5ZXIuVmFyaWFuY2UpKQ0KICBncm91cF9hdmdfdmFyaWFuY2VbaSwzXSA8LSBtZWFuKG5hLm9taXQoYmlnX3NhbXBsZSRQbGF5ZXIuVmFyaWFuY2UpKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1ncm91cF9hdmdfdmFyaWFuY2UsIGFlcyhDT01CT192YXIpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQ09NQk9fdmFyLCAwLjA1KSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQ09NQk9fdmFyLCAwLjk1KSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90KGRhdGE9Z3JvdXBfYXZnX3ZhcmlhbmNlLCBhZXMoU0tJTExfdmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsPSIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkU0tJTExfdmFyLCAwLjA1KSwgY29sb3I9IiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRTS0lMTF92YXIsIDAuOTUpLCBjb2xvcj0iI0NGQjg3QyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChkYXRhPWdyb3VwX2F2Z192YXJpYW5jZSwgYWVzKEJJR192YXIpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGw9IiNBMkE0QTMiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRCSUdfdmFyLCAwLjA1KSwgY29sb3I9IiNBMkE0QTMiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRCSUdfdmFyLCAwLjk1KSwgY29sb3I9IiNBMkE0QTMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoZGF0YT1ncm91cF9hdmdfdmFyaWFuY2UpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKENPTUJPX3ZhciksIGFscGhhPTAuNSwgZmlsbD0iYmxhY2siKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhTS0lMTF92YXIpLCBhbHBoYT0wLjUsIGZpbGw9IiNDRkI4N0MiKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhCSUdfdmFyKSwgYWxwaGE9MC41LCBmaWxsPSIjQTJBNEEzIikgKw0KICBsYWJzKHRpdGxlPSJFc3RpbWF0ZWQgQXZlcmFnZSBWYXJpYW5jZSBpbiBSdW5uaW5nIEltYmFsYW5jZSIsIHg9IkF2ZXJhZ2UgVmFyaWFuY2UgaW4gUnVubmluZyBJbWJhbGFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpgYGB7cn0NCnZhcl9jb25maW50cyA8LSBkYXRhLmZyYW1lKENhdGVnb3J5ID0gYygiQ09NQk8iLCAiU0tJTEwiLCAiQklHIiksDQogICAgICAgICAgICAgICAgICAgICAgIExvd2VyX0JvdW5kID0gYyhxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQ09NQk9fdmFyLCAwLjA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkU0tJTExfdmFyLCAwLjA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQklHX3ZhciwgMC4wNSkpLA0KICAgICAgICAgICAgICAgICAgICAgICBNZWRpYW4gPSBjKHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRDT01CT192YXIsIDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJFNLSUxMX3ZhciwgMC41KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQklHX3ZhciwgMC41KSksDQogICAgICAgICAgICAgICAgICAgICAgIFVwcGVyX0JvdW5kID0gYyhxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQ09NQk9fdmFyLCAwLjk1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRTS0lMTF92YXIsIDAuOTUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJEJJR192YXIsIDAuOTUpKSkNCmhlYWQodmFyX2NvbmZpbnRzKQ0KDQpnZ3Bsb3QoZGF0YT12YXJfY29uZmludHMsIGFlcyh4PUNhdGVnb3J5LCB5PU1lZGlhbiwgeW1pbj1Mb3dlcl9Cb3VuZCwgeW1heD1VcHBlcl9Cb3VuZCwgY29sb3I9Q2F0ZWdvcnkpKSArDQogIGdlb21fcG9pbnRyYW5nZSgpICsNCiAgbGFicyh5PSJFc3RpbWF0ZWQgQXZlcmFnZSBWYXJpYW5jZSBpbiBSdW5uaW5nIEltYmFsYW5jZSIsIHRpdGxlPSJSdW5uaW5nIEltYmFsYW5jZSBWYXJpYW5jZSA5MCUgQ0kiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJDT01CTyIgPSAiYmxhY2siLCAiU0tJTEwiID0gIiNDRkI4N0MiLCAiQklHIiA9ICIjQTJBNEEzIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCmBgYHtyfQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgbXV0YXRlKEFicy5WYWx1ZS5SdW5uaW5nLkltYmFsYW5jZSA9IGFicyhSdW5uaW5nLkltYmFsYW5jZSkpDQoNCkNPTUJPUyA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQ09NQk8iKQ0KDQpTS0lMTFMgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoUG9zaXRpb24gPT0gIlNLSUxMIikNCg0KQklHUyA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQklHIikNCg0KZ3JvdXBfYXZnX2Rpc3QgPC0gZGF0YS5mcmFtZShDT01CT19kaXN0ID0gcmVwKE5BLCA1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU0tJTExfZGlzdCA9IHJlcChOQSwgNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJJR19kaXN0ID0gcmVwKE5BLCA1MDAwKSkNCmBgYA0KDQpgYGB7cn0NCmZvcihpIGluIDE6NTAwMCl7DQogIHNldC5zZWVkKGkpDQogIGNvbWJvX3NhbXBsZSA8LSBzYW1wbGVfbihDT01CT1MsIHNpemU9MTIwMywgcmVwbGFjZT1UUlVFKQ0KICBza2lsbF9zYW1wbGUgPC0gc2FtcGxlX24oU0tJTExTLCBzaXplPTE2MzUsIHJlcGxhY2U9VFJVRSkNCiAgYmlnX3NhbXBsZSA8LSBzYW1wbGVfbihCSUdTLCBzaXplPTEyMjQsIHJlcGxhY2U9VFJVRSkNCiAgDQogIGdyb3VwX2F2Z19kaXN0W2ksMV0gPC0gbWVhbihuYS5vbWl0KGNvbWJvX3NhbXBsZSRBYnMuVmFsdWUuUnVubmluZy5JbWJhbGFuY2UpKQ0KICBncm91cF9hdmdfZGlzdFtpLDJdIDwtIG1lYW4obmEub21pdChza2lsbF9zYW1wbGUkQWJzLlZhbHVlLlJ1bm5pbmcuSW1iYWxhbmNlKSkNCiAgZ3JvdXBfYXZnX2Rpc3RbaSwzXSA8LSBtZWFuKG5hLm9taXQoYmlnX3NhbXBsZSRBYnMuVmFsdWUuUnVubmluZy5JbWJhbGFuY2UpKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1ncm91cF9hdmdfZGlzdCkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoQ09NQk9fZGlzdCksIGFscGhhPTAuNSwgZmlsbD0iYmxhY2siKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhTS0lMTF9kaXN0KSwgYWxwaGE9MC41LCBmaWxsPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoQklHX2Rpc3QpLCBhbHBoYT0wLjUsIGZpbGw9IiNBMkE0QTMiKSArDQogIGxhYnModGl0bGUgPSAiRXN0aW1hdGVkIEF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUgUnVubmluZyBJbWJhbGFuY2UiLCB4PSJBdmVyYWdlIEFic29sdXRlIFZhbHVlIFJ1bm5pbmcgSW1iYWxhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCg0KYGBge3J9DQpkaXN0X2NvbmZpbnRzIDwtIGRhdGEuZnJhbWUoQ2F0ZWdvcnkgPSBjKCJDT01CTyIsICJTS0lMTCIsICJCSUciKSwNCiAgICAgICAgICAgICAgICAgICAgICAgTG93ZXJfQm91bmQgPSBjKHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JENPTUJPX2Rpc3QsIDAuMDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JFNLSUxMX2Rpc3QsIDAuMDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JEJJR19kaXN0LCAwLjA1KSksDQogICAgICAgICAgICAgICAgICAgICAgIE1lZGlhbiA9IGMocXVhbnRpbGUoZ3JvdXBfYXZnX2Rpc3QkQ09NQk9fZGlzdCwgMC41KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfZGlzdCRTS0lMTF9kaXN0LCAwLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JEJJR19kaXN0LCAwLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgVXBwZXJfQm91bmQgPSBjKHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JENPTUJPX2Rpc3QsIDAuOTUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX2Rpc3QkU0tJTExfZGlzdCwgMC45NSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfZGlzdCRCSUdfZGlzdCwgMC45NSkpKQ0KaGVhZChkaXN0X2NvbmZpbnRzKQ0KDQpnZ3Bsb3QoZGF0YT1kaXN0X2NvbmZpbnRzLCBhZXMoeD1DYXRlZ29yeSwgeT1NZWRpYW4sIHltaW49TG93ZXJfQm91bmQsIHltYXg9VXBwZXJfQm91bmQsIGNvbG9yPUNhdGVnb3J5KSkgKw0KICBnZW9tX3BvaW50cmFuZ2UoKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJDT01CTyIgPSAiYmxhY2siLCAiU0tJTEwiID0gIiNDRkI4N0MiLCAiQklHIiA9ICIjQTJBNEEzIikpICsNCiAgbGFicyh0aXRsZT0iQXZlcmFnZSBBYnNvbHV0ZSBWYWx1ZSBSdW5uaW5nIEltYmFsYW5jZSA5MCUgQ0kiLCB5PSJFc3RpbWF0ZWQgQXZlcmFnZSBBYnNvbHV0ZSBWYWx1ZSBSdW5uaW5nIEltYmFsYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7cn0NCkluanVyeV9JbmNpZGVudHMgPC0gZGlzdGluY3QoSW5jaWRlbnRfUmVwb3J0X2NsZWFuWyxjKCJhbm9uX2lkIiwgIlBvc2l0aW9uIiwgIkRhdGUub2YuSW5qdXJ5IildKQ0KDQpQb3NpdGlvbl9jb3VudHMgPC0gbmEub21pdChkaXN0aW5jdChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bLGMoImFub25faWQiLCAiUG9zaXRpb24iKV0pKQ0KDQpnZ3Bsb3QoSW5qdXJ5X0luY2lkZW50cywgYWVzKFBvc2l0aW9uLCBmaWxsPVBvc2l0aW9uKSkgKw0KICBnZW9tX2JhcigpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ09NQk8iID0gImJsYWNrIiwgIlNLSUxMIiA9ICIjQ0ZCODdDIiwgIkJJRyIgPSAiI0EyQTRBMyIpKSArDQogIGxhYnModGl0bGU9IkhhbXN0cmluZyBJbmp1cmllcyBieSBQb3NpdGlvbiIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChQb3NpdGlvbl9jb3VudHMsIGFlcyhQb3NpdGlvbiwgZmlsbD1Qb3NpdGlvbikpICsNCiAgZ2VvbV9iYXIoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNPTUJPIiA9ICJibGFjayIsICJTS0lMTCIgPSAiI0NGQjg3QyIsICJCSUciID0gIiNBMkE0QTMiKSkgKw0KICBsYWJzKHRpdGxlPSJUb3RhbCBQb3NpdGlvbiBDb3VudHMgb24gVGVhbSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3IgUmVtb3ZpbmcgdW5uZWNlc3Nhcnkgb2JqZWN0cyBmcm9tIGVudmlyb25tZW50fQ0KcmVtb3ZlKGJpZ19zYW1wbGUsIEJJR1MsIGNhdGFwdWx0X2luZm8sIGNvbWJvX3NhbXBsZSwgQ09NQk9TLCBjb25maW50cywgZ3JvdXBfYXZnX3ZhcmlhbmNlLA0KICAgICAgIGluY2lkZW50X2luZm8sIGluZm8sIHAsIFBsYXllcl9TdW1tYXJ5X1N0YXRzLCBza2lsbF9zYW1wbGUsIFNLSUxMUywgQklHLCBDT01CTywgaSwNCiAgICAgICBQb3NpdGlvbnMsIFNLSUxMLCBkaXN0X2NvbmZpbnRzLCBncm91cF9hdmdfdmFyaWFuY2UsIGdyb3VwX2F2Z19kaXN0LCBncm91cF9kaXN0YW5jZSwgDQogICAgICAgZ3JvdXBfdmFyaWFuY2VzLCBpbmp1cmVkX2RhdGEsIGluanVyZWRfc2FtcGxlLCBJbmp1cnlfSW5jaWRlbnRzLCBtZWFuX3BsYXllcl92YXJpYW5jZXMsIA0KICAgICAgIFBvc2l0aW9uX2NvdW50cywgdmFyX2NvbmZpbnRzKQ0KYGBgDQoNCg0KIyMgV2hhdCBpcyBhIG1lYW5pbmdmdWwgY2hhbmdlPyBXaGF0IHJlZCBmbGFncyBzaG91bGQgZ28gb2ZmIHdoZW4gd2Ugc2VlIGEgd2Vlay10by13ZWVrIGNoYW5nZSBpbiBydW5uaW5nIGltYmFsYW5jZT8NCg0KICBCYXNlZCBvbiB0aGUgYW5hbHlzaXMgYmVsb3csIHRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhbnkgbWFqb3IgZGlzY2VybmlibGUgZGlmZmVyZW5jZXMgaW4gcnVubmluZyBpbWJhbGFuY2UgYmVmb3JlIG9yIGZvbGxvd2luZyBhbiBpbmp1cnkuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBtYXkgbm90IGJlIGEgZGlyZWN0IGxpbmsgYmV0d2VlbiBIU0kgcmlzayBhbmQgcnVubmluZyBpbWJhbGFuY2UgdmFsdWUgZGlyZWN0bHkgYmVmb3JlaGFuZC4gSW5zdGVhZCwgdGhlIHRyZW5kcyBvZiBtYW55IHBsYXllcnMgd2hvIHdlcmUgaW5qdXJlZCBzZWVtcyB0byBoYXZlIGEgcmVsYXRpdmVseSBjb25zaXN0ZW50IHRyZW5kIG5vdCBlbnRpcmVseSBkZXBlbmRlbnQgb24gdGltZS4gDQogIExvb2tpbmcgYXQgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIGluIHRoZSB3ZWVrcyBsZWFkaW5nIHVwIHRvIGFuZCBmb2xsb3dpbmcgYSBoYW1zdHJpbmcgaW5qdXJ5LCB0aGVyZSBhcmUgYWxzbyBubyBnbGFyaW5nIHRyZW5kcy4gRm9yIHRoaXMgYW5hbHlzaXMsIHdlIGxvb2tlZCBhdCB0aGUgbWVhbiBhbmQgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgbGVhZGluZyB1cCB0byBhbmQgYWZ0ZXIgYW4gaW5qdXJ5IGZvciBhbGwgb2YgdGhlIGluanVyZWQgcGxheWVycyB3aXRoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEuIFRoaXMgc2hvd2VkIHVzIHRoYXQgdGhlcmUgaXMgbm8gY2xlYXIgaW5kaWNhdG9yIG9mIEhTSSByaXNrIGluIHJ1bm5pbmcgaW1iYWxhbmNlIG9yIGFueSBzdW1tYXJ5IHN0YXRpc3RpYyBvZiBpdC4gSW5zdGVhZCBpdCBtYXkgYmUgbW9yZSB1c2VmdWwgdG8gbG9vayBhdCBlYWNoIHBsYXllcidzIHRvdGFsIHJ1bm5pbmcgaW1iYWxhbmNlIGFuZCB0aGVpciBpbmRpdmlkdWFsIHZhcmlhbmNlLiBUaGlzIHNlZW1zIHRvIGJlIG1vcmUgb2YgYSB1c2VmdWwgdG9vbCBmb3IgZGlmZmVyZW50aWF0aW5nIGJldHdlZW4gaW5qdXJlZCBhbmQgdW5pbmp1cmVkIGF0aGxldGVzLg0KDQpgYGB7ciBDYWxjdWxhdGluZyB3ZWVrcyBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBvY2N1cmFuY2UgYmFzZWQgb24gZGF0ZSwgaG93IG1hbnkgaW5qdXJpZXMgcGxheWVyIGhhc30NCiNnZXR0aW5nIHJ1bm5pbmcgaW1iYWxhbmNlcyBmb3IganVzdCBpbmp1cmVkIHBsYXllcnMNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcykNCg0KI21ha2luZyBuZXcgY29sdW1uIHRvIHJlcHJlc2VudCB3aGVuIGluIHRpbWUgaW5qdXJ5IHdvdWxkIGJlLCBuZWdhdGl2ZSBtZWFucyBiZWZvcmUgaW5qdXJ5IGFuZCBwb3NpdGl2ZSBtZWFucyBhZnRlciBpbmp1cnksIDAgbWVhbnMgZGF0ZSBvZiBpbmp1cnkgaWYgdGhlcmUncyBkYXRhIGZvciB0aGF0IGRheQ0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtIHJlcChOQSwgMTY1OCkNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJEluanVyeS5Db3VudCA8LSByZXAoTkEsIDE2NTgpDQoNCiNtYWtpbmcgbmV3IGNvbHVtbiBpbiBpbmNpZGVudCByZXBvcnQgZm9yIHRoZSBpbmp1cnkgY291bnQNCkluY2lkZW50X1JlcG9ydF9jbGVhbiRJbmp1cnkuQ291bnQgPC0gcmVwKE5BLCAxMjIpDQoNCiNnbyB0aHJvdWdoIGFsbCBvZiB0aGUgaW5qdXJlZCBwbGF5ZXJzIGluIHRoZSBkYXRhIHNldA0KZm9yKGkgaW4gMToyMil7DQogICNnZXQgdGhlIGRhdGVzIGVhY2ggcGxheWVyIHdhcyBpbmp1cmVkDQogIGluanVyeV9kYXRlcyA8LSB1bmlxdWUoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KQ0KICANCiAgI2dvIHRocm91Z2ggYWxsIG9mIHRoZSBkYXRlcyBpbiB3aGljaCB0aGUgcGxheWVyIGhhZCBhbiBpbmp1cnkNCiAgZm9yKGogaW4gMTpsZW5ndGgoaW5qdXJ5X2RhdGVzKSl7DQogICAgDQogICAgI2NhbGN1bGF0ZSBkYXRlcyBmb3IgMSwgMiwgMywgNCB3ZWVrcyBiZWZvcmUgYW5kIGFmdGVyIGVhY2ggaW5qdXJ5IGRhdGUNCiAgICBwYXN0XzEgPC0gaW5qdXJ5X2RhdGVzW2pdLTcNCiAgICBwYXN0XzIgPC0gaW5qdXJ5X2RhdGVzW2pdLTE0DQogICAgcGFzdF8zIDwtIGluanVyeV9kYXRlc1tqXS0yMQ0KICAgIHBhc3RfNCA8LSBpbmp1cnlfZGF0ZXNbal0tMjgNCiAgICBmdXR1cmVfMSA8LSBpbmp1cnlfZGF0ZXNbal0rNw0KICAgIGZ1dHVyZV8yIDwtIGluanVyeV9kYXRlc1tqXSsxNA0KICAgIGZ1dHVyZV8zIDwtIGluanVyeV9kYXRlc1tqXSsyMQ0KICAgIGZ1dHVyZV80IDwtIGluanVyeV9kYXRlc1tqXSsyOA0KICAgIA0KICAgICNDYWxjdWxhdGluZyBob3cgbWFueSBpbmp1cmllcyB0aGlzIGlzIGZvciB0aGUgcGxheWVyDQogICAgaW5qdXJ5X2NvdW50IDwtIGFzLmNoYXJhY3RlcigobGVuZ3RoKGluanVyeV9kYXRlcykpIC0gaiArIDEpDQogICAgDQogICAgI2NvbXBhcmUgZGF0ZSBvZiBkYXRhIHBvaW50IGZvciBlYWNoIHBsYXllciB0byBkYXRlIG9mIGluanVyeSwgc3RvcmUgaW4gV2Vla3MuQWZ0ZXIuSW5qdXJ5IGNvbHVtbiwgc3RvcmUgaW5qdXJ5IGNvdW50DQogICAgDQogICAgI2ZpcnN0IHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGluanVyeV9kYXRlc1tqXSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzEsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjEiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGluanVyeV9kYXRlc1tqXSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzEsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI3NlY29uZCB3ZWVrIGFmdGVyIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzIsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjIiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMixdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjdGhpcmQgd2VlayBhZnRlciBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzIgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8zLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICIzIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzMsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI2ZvdXJ0aCB3ZWVrIGFmdGVyIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMyAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzQsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjQiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8zICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfNCxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQgICAgDQogICAgI3dlZWsgcmlnaHQgYmVmb3JlIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBpbmp1cnlfZGF0ZXNbal0gJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMSxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiLTEiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IGluanVyeV9kYXRlc1tqXSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8xLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgICN0d28gd2Vla3MgYmVmb3JlIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzEgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMixdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiLTIiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8yLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICN0aHJlZSB3ZWVrcyBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8zLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItMyINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzMsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgICAgIA0KICAgICNmb3VyIHdlZWtzIGJlZm9yZSBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8zICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzQsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIi00Ig0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfNCxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjRGF0ZSBvZiBJbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID09IGluanVyeV9kYXRlc1tqXSxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMCINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID09IGluanVyeV9kYXRlc1tqXSxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICANCiAgICAjYWRkaW5nIGluanVyeSBjb3VudCB0byBpbmRpY2VudCByZXBvcnQNCiAgICBJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldICYgSW5jaWRlbnRfUmVwb3J0X2NsZWFuJERhdGUub2YuSW5qdXJ5PT1pbmp1cnlfZGF0ZXNbal0sXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogIH0NCn0NCg0KDQojbWFraW5nIHdlZWtzIGFmdGVyIGluanVyeSBhbmQgaW5qdXJ5IGNvdW50IGludG8gYSBmYWN0b3IgYW5kIGNvbWJpbmluZyBkYXRhIHNldHMgYmFjayB0b2dldGhlcg0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgPC0gSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgJT4lDQogIG11dGF0ZShXZWVrcy5BZnRlci5Jbmp1cnkgPSBmYWN0b3IoV2Vla3MuQWZ0ZXIuSW5qdXJ5KSwNCiAgICAgICAgIEluanVyeS5Db3VudCA9IGZhY3RvcihJbmp1cnkuQ291bnQpKQ0KDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gbGVmdF9qb2luKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcpDQoNCiNnZXR0aW5nIHJpZCBvZiBqdW5rIHRoYXQgd2FzIGZyb20gdGhlIGxvb3ANCnJlbW92ZShmdXR1cmVfMSwgZnV0dXJlXzIsIGZ1dHVyZV8zLCBmdXR1cmVfNCwgaSwgaW5qdXJ5X2NvdW50LCBpbmp1cnlfZGF0ZXMsIGosIHBhc3RfMSwgcGFzdF8yLCBwYXN0XzMsIHBhc3RfNCkNCnJlbW92ZShJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZykNCmBgYA0KDQojIyMgSW5qdXJ5IFJpc2sNCmBgYHtyfQ0KZm9yKGkgaW4gMToyMil7DQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogICAgZ2VvbV9saW5lKGxpbmV0eXBlPTEpICsNCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1XZWVrcy5BZnRlci5Jbmp1cnkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5LCBsaW5ldHlwZT0yKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIi00Ij0iZ3JlZW4iLCAiLTMiPSJ5ZWxsb3ciLCAiLTIiPSJvcmFuZ2UiLCAiLTEiPSJyZWQiLCAiMCI9ImJsYWNrIiwiMSI9ICJwdXJwbGUiLCAiMiI9Im5hdnkiLCAiMyI9ImJsdWUiLCAiNCI9InNreWJsdWUiKSkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZT0iUnVubmluZyBJbWJhbGFuY2UiLCBzdWJ0aXRsZSA9IGluanVyZWRfSURzW2ldKQ0KICANCiAgcHJpbnQocCkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KI21ha2luZyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgcmVsYXRpdmUgdG8gaW5qdXJ5DQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCBJbmp1cnkuQ291bnQsIFdlZWtzLkFmdGVyLkluanVyeSkgJT4lDQogIG11dGF0ZShXZWVrcy5BZnRlci5Jbmp1cnkuVmFyaWFiaWxpdHkgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpLA0KICAgICAgICAgV2Vla3MuQWZ0ZXIuSW5qdXJ5Lk1lYW4gPSBtZWFuKGFicyhSdW5uaW5nLkltYmFsYW5jZSkpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZm9yKGkgaW4gMToyMil7DQogICNsb29raW5nIGF0IG1lYW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgYmVmb3JlIGFuZCBhZnRlciBpbmp1cnkgZm9yIGVhY2ggaW5qdXJlZCBwbGF5ZXINCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gaW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFdlZWtzLkFmdGVyLkluanVyeS5NZWFuLCBncm91cD1Jbmp1cnkuQ291bnQpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIHhsaW0obWluKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSktMzAsIG1heChJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpKzMwKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkgKw0KICAgIGxhYnModGl0bGU9IkF2ZXJhZ2UgQWJzb2x1dGUgRGlzdGFuY2UgcGVyIFdlZWsiLGluanVyZWRfSURzW2ldKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIi00Ij0iZ3JlZW4iLCAiLTMiPSJ5ZWxsb3ciLCAiLTIiPSJvcmFuZ2UiLCAiLTEiPSJyZWQiLCAiMCI9ImJsYWNrIiwiMSI9ICJwdXJwbGUiLCAiMiI9Im5hdnkiLCAiMyI9ImJsdWUiLCAiNCI9InNreWJsdWUiKSkNCiAgDQogIHByaW50KHApDQp9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KZm9yKGkgaW4gMToyMil7DQogICNsb29raW5nIGF0IHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIGJlZm9yZSBhbmQgYWZ0ZXIgaW5qdXJ5IGZvciBlYWNoIGluanVyZWQgcGxheWVyDQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0sIGFlcyhEYXRlLCBXZWVrcy5BZnRlci5Jbmp1cnkuVmFyaWFiaWxpdHksIGdyb3VwPUluanVyeS5Db3VudCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1XZWVrcy5BZnRlci5Jbmp1cnkpKSArDQogICAgeGxpbShtaW4oSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KS0zMCwgbWF4KEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkrMzApICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KSArDQogICAgbGFicyh0aXRsZT0iVmFyaWFuY2UgaW4gUnVubmluZyBJbWJhbGFuY2UgcGVyIFdlZWsiLCBzdWJ0aXRsZT1pbmp1cmVkX0lEc1tpXSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCItNCI9ImdyZWVuIiwgIi0zIj0ieWVsbG93IiwgIi0yIj0ib3JhbmdlIiwgIi0xIj0icmVkIiwgIjAiPSJibGFjayIsIjEiPSAicHVycGxlIiwgIjIiPSJuYXZ5IiwgIjMiPSJibHVlIiwgIjQiPSJza3libHVlIikpDQogIA0KICBwcmludChwKQ0KfQ0KYGBgDQoNCiMjIyBQbGF5ZXIgVHJlbmRzDQpgYGB7ciBMb29raW5nIGF0IGRldGVjdGFibGUgdHJlbmRzIGluIHBsYXllciBkYXRhfQ0KZm9yKGkgaW4gMTo3MSl7DQogICNvbmx5IHBsb3R0aW5nIGlmIHRoZXJlIGFyZSBvdmVyIDE1IGRhdGEgcG9pbnRzIGZvciBlYWNoIHBsYXllcg0KICBpZihucm93KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0pPjE1KXsNCiAgICAjY2FsY3VsYXRpbmcgaG93IHN0cm9uZyBvZiBhIG5vbiBsaW5lYXIgdHJlbmQgdGhlcmUgaXMgdXNpbmcgYSBnYW0NCiAgICBkZiA8LSBzdW1tYXJ5KGdhbShSdW5uaW5nLkltYmFsYW5jZX5zKERheXMuU2luY2UuU3RhcnQpLA0KICAgICAgICAgICAgICBkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0pKSRlZGYNCiAgICAjY2FsY3VsYXRpbmcgaG93IHN0cm9uZyBvZiBhIGxpbmVhciB0cmVuZCB0aGVyZSBpcyB1c2luZyBrZW5kYWxsIGNvcnJlbGF0aW9uDQogICAgS2VuZGFsbF9Db3IgPC0gY29yKHg9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSREYXlzLlNpbmNlLlN0YXJ0LCB5PUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0kUnVubmluZy5JbWJhbGFuY2UsIG1ldGhvZD0ia2VuZGFsbCIpDQogIA0KICAgICNpZiB0aGVyZSBpcyBhIGRldGVjdGFibGUgbGluZWFyIHJlbGF0aW9uc2hpcCBpbiB0aGUgZGF0YQ0KaWYoYWJzKEtlbmRhbGxfQ29yPjAuMikgJiBkZjw9Myl7ICNsaW5lYXIgdHJlbmQgaW4gZGF0YQ0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX3Ntb290aChjb2xvcj0gInJlZCIsDQogICAgICAgICAgICAgICAgbWV0aG9kPSJsbSIsDQogICAgICAgICAgICAgICAgc2U9RkFMU0UpICsNCiAgICBsYWJzKHRpdGxlPSJSdW5uaW5nIEltYmFsYW5jZSBTaW5jZSBKYW51YXJ5IDEsIDIwMjQgKGxpbmVhciB0cmVuZCkiLCBzdWJ0aXRsZT1hbGxfSURzW2ldLCkgKw0KICAgIHhsaW0oYXMuRGF0ZSgiMjAyNS0wMS0wMSIsICIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDI1LTA1LTAxIiwgIiVZLSVtLSVkIikpDQogIA0KICBwcmludChwKQ0KfQ0KICAgICNpZiB0aGVyZSBpcyBhIGRldGVjdGFibGUgbm9uLWxpbmVhciB0cmVuZCBpbiB0aGUgZGF0YQ0KICBpZihkZiA+IDMpeyAjbm9uIGxpbmVhciB0cmVuZCBpbiBkYXRhDQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fc21vb3RoKGNvbG9yID0gInNreWJsdWUiLA0KICAgICAgICAgICAgICAgIHNlPUZBTFNFKSArDQogICAgbGFicyh0aXRsZT0iUnVubmluZyBJbWJhbGFuY2UgU2luY2UgSmFudWFyeSAxLCAyMDI0IChub24tbGluZWFyIHRyZW5kKSIsIHN1YnRpdGxlPWFsbF9JRHNbaV0sKSArDQogICAgeGxpbShhcy5EYXRlKCIyMDI1LTAxLTAxIiwgIiVZLSVtLSVkIiksIGFzLkRhdGUoIjIwMjUtMDUtMDEiLCAiJVktJW0tJWQiKSkNCiAgDQogIHByaW50KHApDQogIH0NCiAgICAjaWYgdGhlcmUgaXMgbm8gZGV0ZWN0YWJsZSB0cmVuZCBpbiB0aGUgZGF0YQ0KICAgIGlmKGRmPD0zICYgYWJzKEtlbmRhbGxfQ29yKTw9MC4yKXsNCiAgICAgIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGxhYnModGl0bGU9IlJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCAobm9uLWxpbmVhciB0cmVuZCkiLCBzdWJ0aXRsZT1hbGxfSURzW2ldLCkgKw0KICAgIHhsaW0oYXMuRGF0ZSgiMjAyNS0wMS0wMSIsICIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDI1LTA1LTAxIiwgIiVZLSVtLSVkIikpDQogICAgfQ0KICB9DQp9DQoNCiNwbG90dGVkIG9ubHkgZnJvbSBKYW51YXJ5IDEsIDIwMjUgdG8gTWF5IDEsIDIwMjUNCmBgYA0KDQoNCiMjIyBEZXRlY3RhYmxlIFRyZW5kIHdpdGggSW5qdXJ5IFJpc2sNCmBgYHtyfQ0KdHJlbmRzIDwtIGRhdGEuZnJhbWUoSUQgPSBhbGxfSURzLA0KICAgICAgICAgICAgICAgICAgICAgS0MgPSByZXAoMCwgNzEpKQ0KZm9yKGkgaW4gMTo3MSl7DQogICNvbmx5IHBsb3R0aW5nIGlmIHRoZXJlIGFyZSBvdmVyIDE1IGRhdGEgcG9pbnRzIGZvciBlYWNoIHBsYXllcg0KICBpZihucm93KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0pPjE1KXsNCiAgICAjY2FsY3VsYXRpbmcgaG93IHN0cm9uZyBvZiBhIG5vbiBsaW5lYXIgdHJlbmQgdGhlcmUgaXMgdXNpbmcgYSBnYW0NCiAgICBkZiA8LSBzdW1tYXJ5KGdhbShSdW5uaW5nLkltYmFsYW5jZX5zKERheXMuU2luY2UuU3RhcnQpLA0KICAgICAgICAgICAgICBkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0pKSRlZGYNCiAgICAjY2FsY3VsYXRpbmcgaG93IHN0cm9uZyBvZiBhIGxpbmVhciB0cmVuZCB0aGVyZSBpcyB1c2luZyBrZW5kYWxsIGNvcnJlbGF0aW9uDQogICAgS2VuZGFsbF9Db3IgPC0gY29yKHg9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSREYXlzLlNpbmNlLlN0YXJ0LCB5PUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0kUnVubmluZy5JbWJhbGFuY2UsIG1ldGhvZD0ia2VuZGFsbCIpDQogICAgDQogICAgdHJlbmRzW3RyZW5kcyRJRCA9PSBhbGxfSURzW2ldLDJdID0gS2VuZGFsbF9Db3INCiAgDQogICAgaWYoYWxsX0lEc1tpXSAlaW4lIGluanVyZWRfSURzKXsNCiAgICAgIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSwNCiAgICAgICAgICAgICBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogICAgICAgIGdlb21fcG9pbnQoY29sb3I9InNreWJsdWUiKSArDQogICAgICAgIGdlb21fbGluZShjb2xvcj0ic2t5Ymx1ZSIpICsNCiAgICAgICAgbGFicyhzdWJ0aXRsZT1LZW5kYWxsX0NvcikNCiAgICB9DQogICAgaWYoIShhbGxfSURzW2ldICVpbiUgaW5qdXJlZF9JRHMpKXsNCiAgICAgIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGFsbF9JRHNbaV0sXSwNCiAgICAgICAgICAgICBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIGdlb21fbGluZSgpICsNCiAgICAgICAgbGFicyhzdWJ0aXRsZT1LZW5kYWxsX0NvcikNCiAgICB9DQogICAgDQogICAgcHJpbnQocCkNCiAgfQ0KfQ0KDQojcGxvdHRlZCBvbmx5IGZyb20gSmFudWFyeSAxLCAyMDI1IHRvIE1heSAxLCAyMDI1DQpgYGANCg0KDQpgYGB7cn0NCnRyZW5kcyA8LSB0cmVuZHMgJT4lDQogIG11dGF0ZShpbmp1cmVkID0gaWZlbHNlKElEICVpbiUgaW5qdXJlZF9JRHMsMSwwKSkNCg0KZ2dwbG90KHRyZW5kc1t0cmVuZHMkS0M+MCAmIHRyZW5kcyRpbmp1cmVkPT0xLF0sIGFlcyhLQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KDQpnZ3Bsb3QodHJlbmRzW3RyZW5kcyRLQz4wICYgdHJlbmRzJGluanVyZWQ9PTAsXSwgYWVzKEtDKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KaW5qdXJlZF90cmVuZHMgPC0gdHJlbmRzICU+JQ0KICBmaWx0ZXIoaW5qdXJlZD09MSkNCg0KdW5pbmp1cmVkX3RyZW5kcyA8LSB0cmVuZHMgJT4lDQogIGZpbHRlcihpbmp1cmVkPT0wKQ0KDQoNCmtlbmRhbGxfY29ycyA8LSBkYXRhLmZyYW1lKGluanVyZWRfYXZnX0tDID0gcmVwKE5BLDUwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pbmp1cmVkX2F2Z19LQyA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGF2Z19kaWZmX0tDID0gcmVwKE5BLDUwMDApKQ0KDQpmb3IoaSBpbiAxOjUwMDApew0KICBzZXQuc2VlZChpKQ0KICBpbmp1cmVkX3NhbXAgPC0gc2FtcGxlX24oaW5qdXJlZF90cmVuZHMsIHNpemU9MjIsIHJlcGxhY2U9VFJVRSkNCiAgdW5pbmp1cmVkX3NhbXAgPC0gc2FtcGxlX24odW5pbmp1cmVkX3RyZW5kcywgc2l6ZT00OSwgcmVwbGFjZT1UUlVFKQ0KICANCiAga2VuZGFsbF9jb3JzW2ksMV0gPC0gbWVhbihhYnMoaW5qdXJlZF9zYW1wJEtDKSkNCiAga2VuZGFsbF9jb3JzW2ksMl0gPC0gbWVhbihhYnModW5pbmp1cmVkX3NhbXAkS0MpKQ0KICBrZW5kYWxsX2NvcnNbaSwzXSA8LSBrZW5kYWxsX2NvcnNbaSwxXSAtIGtlbmRhbGxfY29yc1tpLDJdDQp9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9a2VuZGFsbF9jb3JzLCBhZXMoYXZnX2RpZmZfS0MpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShrZW5kYWxsX2NvcnMkYXZnX2RpZmZfS0MsIDAuMDUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShrZW5kYWxsX2NvcnMkYXZnX2RpZmZfS0MsIDAuOTUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHRpdGxlID0gIkVzdGltYXRlZCBEaWZmZXJlbmNlIGluIEF2ZXJhZ2UgS2VuZGFsbCBSYW5rIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IiwNCiAgICAgICB4ID0gIkRpZmZlcmVuY2UgaW4gQXZlcmFnZSBLZW5kYWxsIENvZWZmaWNpZW50IikNCg0KZ2dwbG90KGRhdGE9a2VuZGFsbF9jb3JzKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhpbmp1cmVkX2F2Z19LQyksIGFscGhhPTAuNywgZmlsbD0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHVuaW5qdXJlZF9hdmdfS0MpLCBhbHBoYT0wLjcpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgQXZlcmFnZSBLZW5kYWxsIFJhbmsgQ29ycmVsYXRpb24gQ29lZmZpY2llbnQiLA0KICAgICAgIHggPSAiQXZlcmFnZSBLZW5kYWxsIENvZWZmaWNpZW50IikNCmBgYA0KDQoNCmBgYHtyIHJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZnJvbSBzZWN0aW9ufQ0KcmVtb3ZlKGksIHAsIGRmLCBLZW5kYWxsX0Nvciwga2VuZGFsbF9jb3JzLCB0cmVuZHMpDQpgYGANCg0KDQojIyBJcyBydW5uaW5nIGltYmFsYW5jZSBzZW5zaXRpdmUgZW5vdWdoIG9mIGEgbWV0cmljIHRvIHVzZSBhcyBhIHByb2dub3NpcyB0b29sIHZlcnN1cyBhIHJlaGFiIHRvb2w/DQoNCiAgQmFzZWQgb24gdGhlIGFuYWx5c2lzIGJlbG93LCB3ZSBjYW4gc2VlIHRoYXQgYnkgc29sZWx5IHVzaW5nIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZyb20gdGhlIHRpbWUgb2YgdGhlIGluanVyeSB0byB0aGUgZW5kIG9mIHRoZSBwcmVkaWN0ZWQgcmV0dXJuLXRvLXBsYXkgcmFuZ2UgaXMgbm90IGEgdmVyeSBzdHJvbmcgcHJlZGljdG9yIGZvciB3aGV0aGVyIG9yIG5vdCBpdCB3aWxsIHRha2UgbG9uZ2VyIGZvciBhIHBsYXllciB0byByZWNvdmVyIG9yIG5vdC4gSW4gdGhpcyBhbmFseXNpcywgd2UgdXNlZCB0aGUgcnVubmluZyBpbWJhbGFuY2UgaW4gdGhlIHRpbWUgZnJhbWUgc3RhcnRpbmcgd2l0aCB0aGUgaW5qdXJ5IGRhdGUgdG8gdGhlIGVuZCBvZiB0aGUgcHJvZ25vc2lzIHRpbWUgZnJhbWUuIFdpdGggdGhlc2Ugc3BlY2lmaWMgcnVubmluZyBpbWJhbGFuY2UgdmFsdWVzLCB3ZSBjYWxjdWxhdGVkIHRoZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBhbmQgd2hldGhlciBvciBub3QgdGhlIGF0aGxldGUgcmV0dXJuZWQgdG8gcGxheSB3aXRoaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIG9yIG5vdC4gDQogIFRoaXMgYW5hbHlzaXMgZm91bmQgdGhhdCB3aGVuIHVzaW5nIHRoZSB2YXJpYW5jZSBpbiB0aGVzZSB0aW1lIGZyYW1lcyBhcyB0aGUgb25seSBwcmVkaWN0b3IgaW4gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLCB0aGUgc2xvcGUgY29lZmZpY2llbnQgYXNzb2NpYXRlZCB3aXRoIHRoZSB2YXJpYW5jZSB3YXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhdCB0aGUgJFxhbHBoYSA9IDAuMDEkIHNpZ25pZmljYW5jZSBsZXZlbC4gV2l0aCB0aGF0IHRob3VnaCwgdGhlIGNyb3NzIHZhbGlkYXRlZCBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgd2FzIG9ubHkgYXJvdW5kIDAuNiBzdWdnZXN0aW5nIHRoYXQgaXQgd2Fzbid0IHN1cGVyIHN0cm9uZyBpbiBwcmFjdGljZS4NCiAgSW4gb3JkZXIgdG8gdW5kZXJzdGFuZCB0aGUgaW1wYWN0IHRoYXQgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgaGFkIG9uIHdoZXRoZXIgb3Igbm90IGEgcGxheWVyIHJldHVybmVkIGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSBvciBub3QsIHdlIHBlcmZvcm1lZCBhIGJvb3RzdHJhcC4gV2Ugc2VwYXJhdGVkIHRoZSBvYnNlcnZhdGlvbnMgaW4gd2hpY2ggcGxheWVycyBkaWQgYW5kIGRpZCBub3QgcmV0dXJuIGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSBpbnRvIHR3byBkaWZmZXJlbnQgZGF0YSBzZXRzLiBXZSB0aGVuIHNhbXBsZWQgZnJvbSBlYWNoIG9mIHRoZXNlIHR3byBkYXRhIHNldHMgYW5kIGNhbGN1bGF0ZWQgdGhlIGF2ZXJhZ2UgdmFyaWFuY2UgZm9yIGVhY2ggc2FtcGxlLiBUaGlzIHdhcyByZXBlYXRlZCA1MDAwIHRpbWVzLiBUaGlzIGdhdmUgdXMgYW4gZXN0aW1hdGUgb2YgdGhlIGF2ZXJhZ2UgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgZm9yIHBsYXllcnMgd2hvIHJldHVybmVkIHdpdGhpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUgYW5kIHRob3NlIHdobyBkaWQgbm90Lg0KICBUaGlzIGJvb3RzdHJhcCByZXZlYWxlZCB0aGF0IHBsYXllcnMgd2hvIGRpZCBub3QgcmV0dXJuIHdpdGhpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUgaGFkIGEgdmFyaWFuY2UgZ3JlYXRlciBieSByb3VnaGx5IDEuMiBkdXJpbmcgdGhlaXIgdGltZSBvZiByZWNvdmVyeSB0aGFuIHRob3NlIHdobyByZXR1cm5lZCBvbiB0aW1lLiBUaGlzIHRlbGxzIHVzIHRoYXQgd2hpbGUgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgaXMgbm90IGRpcmVjdGx5IHN0cm9uZyBlbm91Z2ggdG8gcHJlZGljdCB3aGV0aGVyIG9yIG5vdCBhbiBhdGhsZXRlIHdpbGwgcmV0dXJuIHdpdGhpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUsIGl0IGNhbiBiZSB1c2VkIHRvIHN1cHBsZW1lbnQgcHJvZ25vc2lzIG9yIG1ha2UgYWRqdXN0bWVudHMgdG8gdGhlIHByb2dub3NpcyBkdXJpbmcgdGhlIHRpbWUgb2YgcmVjb3Zlcnkgb2YgYSBIU0kuIA0KDQpgYGB7cn0NCiNDYWxjdWxhdGluZyBkYXRlIGJhY2sgdG8gcGxheSBpbiBpbmNpZGVudCByZXBvcnQgZGF0YSBzZXQNCkluY2lkZW50X1JlcG9ydF9jbGVhbiA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW4gJT4lDQogIGZpbHRlcighaXMubmEoSW5qdXJ5LlByb2dub3NpcykpJT4lDQogICNjYWxjdWxhdGluZyBob3cgbG9uZyBwcmVkaWN0ZWQgdGltZSBsb3NzIGlzIGJhc2VkIG9uIHByb2dub3Npcw0KICAgICAgICAgI2JlZ2lubmluZyBvZiBwcmVkaWN0ZWQgcmFuZ2Ugb2YgcmV0dXJuDQogIG11dGF0ZShFeHBlY3RlZC5TdGFydC5SZXR1cm4gPSBhcy5EYXRlKGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iTm8gRXhwZWN0ZWQgVGltZSBMb3NzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ikxlc3MgdGhhbiAxIFdlZWsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSIxLTQgV2Vla3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cyg3KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoMjgpKSkpKSwNCiAgICAgICAgICNlbmQgb2YgcHJlZGljdGVkIHJhbmdlIG9mIHJldHVybg0KICAgICAgICAgRXhwZWN0ZWQuRW5kLlJldHVybiA9IGFzLkRhdGUoaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJObyBFeHBlY3RlZCBUaW1lIExvc3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iTGVzcyB0aGFuIDEgV2VlayIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoNyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iMS00IFdlZWtzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoMjgpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cyg1NikpKSkpKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgRGF0ZS5vZi5Jbmp1cnkpICU+JQ0KICAjY2FsY3VsYXRpbmcgYWN0dWFsIGRhdGUgY2xlYXJlZCB0byByZXR1cm4NCiAgbXV0YXRlKEFjdHVhbC5SZXR1cm4gPSBEYXRlLm9mLkluanVyeStkYXlzKHN1bShuYS5vbWl0KERheXMuaW4uU3RhdHVzKSkpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBMb29raW5nIGF0IGlmIHJldHVybiB0byBwbGF5IHdhcyBpbiBwcmVkaWN0ZWQgcmFuZ2UgZm9yIGVhY2ggcGxheWVyfQ0KZm9yKGkgaW4gMToyMil7DQogICNsb29raW5nIGF0IG1lYW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgYmVmb3JlIGFuZCBhZnRlciBpbmp1cnkgZm9yIGVhY2ggaW5qdXJlZCBwbGF5ZXINCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gaW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2xpbmUoKSArDQogICAgI01hcmtpbmcgd2hlbiBpbmp1cnkgb2NjdXJyZWQgd2l0aCBnb2xkIGxpbmUNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PUluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSwgY29sb3I9IiNDRkI4N0MiKSArDQogICAgI01hcmtpbmcgYWN0dWFsIHJldHVybiBkYXRlIHdpdGggc29saWQgYnJvd24gbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEFjdHVhbC5SZXR1cm4sIGNvbG9yPSIjOEQ3MzM0IiwgbGluZXR5cGU9MSkgKw0KICAjTWFya2luZyBiZWdpbm5pbmcgb2YgcHJlZGljdGVkIHJldHVybiB0byBwbGF5IHJhbmdlIHdpdGggZ3JleSBkb3R0ZWQgbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLlN0YXJ0LlJldHVybiwgY29sb3I9IiNBMkE0QTMiLCBsaW5ldHlwZT0zKSArDQogICAgI01hcmtpbmcgZW5kIG9mIHByZWRpY3RlZCByZXR1cm4gdG8gcGxheSByYW5nZSB3aXRoIGdyZXkgZG90dGVkIGxpbmUNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PUluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSRFeHBlY3RlZC5FbmQuUmV0dXJuLCBjb2xvcj0iI0EyQTRBMyIsIGxpbmV0eXBlPTMpICsNCiAgICBsYWJzKHRpdGxlPWluanVyZWRfSURzW2ldKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCANCiAgICAgICAgICAgICB4bWluID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLlN0YXJ0LlJldHVybiwNCiAgICAgICAgICAgICB4bWF4ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLkVuZC5SZXR1cm4sDQogICAgICAgICAgICAgeW1pbj1taW4oSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0kUnVubmluZy5JbWJhbGFuY2UpLCB5bWF4PW1heChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gaW5qdXJlZF9JRHNbaV0sXSRSdW5uaW5nLkltYmFsYW5jZSksIGFscGhhPTAuMykgKw0KICAgIHRoZW1lX21pbmltYWwoKQ0KICANCiAgcHJpbnQocCkNCn0NCg0KYGBgDQoNCg0KDQpgYGB7cn0NCiNsb29raW5nIGF0IHJ1bm5pbmcgaW1iYWxhbmNlIG9mIG9ubHkgaW5qdXJlZCBwbGF5ZXJzDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMpDQoNCiNNYWtpbmcgYmluYXJ5IGNvbHVtbiBpZiBkYXRlIHdhcyBpbiB0aW1lIHJhbmdlIG9mIGluanVyeSBwcm9nbm9zaXMNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUuaW4uUmFuZ2UgPC0gcmVwKDAsIDE2NTgpDQoNCiNnbyB0aHJvdWdoIGFsbCBvZiB0aGUgaW5qdXJlZCBwbGF5ZXJzIGluIHRoZSBkYXRhIHNldA0KZm9yKGkgaW4gMToyMil7DQogICNnZXQgdGhlIGRhdGVzIGVhY2ggcGxheWVyIHdhcyBpbmp1cmVkDQogIGluanVyeV9kYXRlcyA8LSB1bmlxdWUoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KQ0KICANCiAgI2dvIHRocm91Z2ggZGF0ZXMgb2YgaW5qdXJ5IGZvciBlYWNoIHBsYXllcg0KICBmb3IoaiBpbiAxOmxlbmd0aChpbmp1cnlfZGF0ZXMpKXsNCiAgICBpZihpbmp1cmVkX0lEc1tpXSA9PSAiSURfNTAiKXsgI0lEXzUwIGRvZXMgbm90IGhhdmUgZW5vdWdoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGENCiAgICAgIGJyZWFrDQogICAgfQ0KICAgICNnZXQgdGhlIGV4cGVjdGVkIGRhdGUgb2YgcmV0dXJuIGZvciB0aGF0IGluc3RhbmNlIG9mIGluanVyeQ0KICAgIGV4cGVjdGVkX3JldHVybiA8LSBhcy5EYXRlKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJiBJbmNpZGVudF9SZXBvcnRfY2xlYW4kRGF0ZS5vZi5Jbmp1cnk9PWluanVyeV9kYXRlc1tqXSxdJEV4cGVjdGVkLkVuZC5SZXR1cm5bMV0pDQogICAgDQogICAgI2lmIHRoZSBkYXRlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGlzIGJldHdlZW4gZGF5IG9mIGluanVyeSBhbmQgbGFzdCBkYXkgb2YgcHJlZGljdGlvbiwgc2V0IGFzIDENCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPj0gaW5qdXJ5X2RhdGVzW2pdICYgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8PSBleHBlY3RlZF9yZXR1cm4sXSREYXRlLmluLlJhbmdlIDwtIDENCiAgfQ0KfQ0KDQojbWFraW5nIGEgY29sdW1uIGZvciB0aGUgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgZm9yIGVhY2ggaW5qdXJ5IGluc3RhbmNlIHJhbmdlDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyA8LSBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyAlPiUNCiAgZmlsdGVyKERhdGUuaW4uUmFuZ2UgPT0gMSkgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIEluanVyeS5Db3VudCkgJT4lDQogIG11dGF0ZShJbmp1cnkuVmFyaWFiaWxpdHkgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiNhZGRpbmcgaW5qdXJ5IGFuZCBydW5uaW5nIGRhdGEgc2V0cyB0b2dldGhlcg0KSW5qdXJlZF9EYXRhIDwtIGxlZnRfam9pbihJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZywgSW5jaWRlbnRfUmVwb3J0X2NsZWFuLCBieT1jKCJhbm9uX2lkIiwgIkluanVyeS5Db3VudCIpLCByZWxhdGlvbnNoaXAgPSAibWFueS10by1tYW55IikgJT4lDQogICNtYWtpbmcgbmV3IGNvbHVtbiBpZiBwbGF5ZXIgcmV0dXJuZWQgaW4gcHJlZGljdGVkIHRpbWUgZnJhbWUNCiAgbXV0YXRlKFJldHVybi5pbi5SYW5nZSA9IGlmZWxzZShBY3R1YWwuUmV0dXJuPj1FeHBlY3RlZC5TdGFydC5SZXR1cm4gJiBBY3R1YWwuUmV0dXJuIDw9IEV4cGVjdGVkLkVuZC5SZXR1cm4sIDEsIDApKSAlPiUNCiAgI3JlbW92aW5nIHJvd3MgdGhhdCBhcmUgbWlzc2luZyBpbXBvcnRhbnQgZGF0YQ0KICBmaWx0ZXIoIWlzLm5hKEluanVyeS5WYXJpYWJpbGl0eSksDQogICAgICAgICAhaXMubmEoUmV0dXJuLmluLlJhbmdlKSkNCmBgYA0KDQoNCiMjIyBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsDQpgYGB7cn0NCnNldC5zZWVkKDEwMDApDQojbWFraW5nIGEgNzUlIHRvIDI1JSB0cmFpbmluZyB0byB0ZXN0aW5nIHNwbGl0DQpyb3dzIDwtIHNhbXBsZSgxOm5yb3coSW5qdXJlZF9EYXRhKSwgc2l6ZT0obnJvdyhJbmp1cmVkX0RhdGEpKjAuNzUpLCByZXBsYWNlPUZBTFNFKQ0KSW5qdXJlZF90cmFpbiA8LSBJbmp1cmVkX0RhdGFbcm93cyxdDQpJbmp1cmVkX3Rlc3QgPC0gSW5qdXJlZF9EYXRhWy1yb3dzLF0NCg0KI2J1aWxkaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgZnJvbSB0cmFpbmluZyBkYXRhDQpyZXR1cm5fdG9fcGxheV9tb2RlbCA8LSBnbG0oUmV0dXJuLmluLlJhbmdlfkluanVyeS5WYXJpYWJpbGl0eSwgZGF0YT1Jbmp1cmVkX3RyYWluLCBmYW1pbHk9ImJpbm9taWFsIikNCg0KI2xvb2tpbmcgYXQgY29lZmZpY2llbnRzIGFuZCBwLXZhbHVlcw0Kc3VtbWFyeShyZXR1cm5fdG9fcGxheV9tb2RlbCkNCg0KI21ha2luZyBwcmVkaWN0aW9ucyBvbiB0ZXN0aW5nIGRhdGEgYmFzZWQgb2ZmIG9mIHRoZSBtb2RlbCBidWlsdA0KSW5qdXJlZF90ZXN0IDwtIEluanVyZWRfdGVzdCAlPiUNCiAgbXV0YXRlKFByZWRpY3Rpb24gPSBpZmVsc2UocHJlZGljdChyZXR1cm5fdG9fcGxheV9tb2RlbCwgbmV3ZGF0YT1Jbmp1cmVkX3Rlc3QsIHR5cGU9InJlc3BvbnNlIik+MC41LCAxLCAwKSkNCg0KI2NhbGN1bGF0aW5nIENFUg0KSW5qdXJlZF90ZXN0ICU+JQ0KICBzdW1tYXJpemUoQ0VSID0gbWVhbihQcmVkaWN0aW9uICE9IFJldHVybi5pbi5SYW5nZSkpDQoNCmBgYA0KDQoNCmBgYHtyIENyb3NzIHZhbGlkYXRpbmcgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbH0NCiNtYWtpbmcgbW9kZWwgd2l0aCBhbGwgb2YgdGhlIGRhdGENCnJldHVybl90b19wbGF5X2N2IDwtIGdsbShSZXR1cm4uaW4uUmFuZ2V+SW5qdXJ5LlZhcmlhYmlsaXR5LCBkYXRhPUluanVyZWRfRGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpDQoNCiNtYWtpbmcgY29zdCBmdW5jdGlvbiBmb3IgQ0VSDQpjb3N0IDwtIGZ1bmN0aW9uKG9icywgcHJlZCl7DQogIG1lYW4oKHByZWQgPD0gMC41KSAmIG9icz09MSB8IChwcmVkID4gMC41KSAmIG9icz09MCkNCn0NCg0Kc2V0LnNlZWQoMTAwMCkNCg0KI2Nyb3NzIHZhbGlkYXRpbmcgd2l0aCBLPTEwDQp0ZW5fY3YgPC0gY3YuZ2xtKGRhdGE9SW5qdXJlZF9EYXRhLGdsbWZpdD1yZXR1cm5fdG9fcGxheV9jdixjb3N0LEs9MTApDQoNCiNleHRyYWN0IGF2ZXJhZ2UgZXJyb3INCnRlbl9jdiRkZWx0YVsxXQ0KYGBgDQoNCg0KIyMjIEJvb3RzdHJhcHBpbmcgRGlmZmVyZW5jZXMgQmV0d2VlbiBJbiBhbmQgT3V0IG9mIFJhbmdlIFJldHVybiB0byBQbGF5cw0KYGBge3IgTWFraW5nIHNlcGFyYXRlIGRhdGEgc2V0cyBmb3IgYm9vdHN0cmFwcGVkIGNvbXBhcmlzb259DQojdGFraW5nIG9ubHkgcGxheWVycyB3aG8gcmVjb3ZlcmVkIGluIHByZWRpY3RlZCB0aW1lIGZyYW1lDQpJbl9SYW5nZSA8LSBJbmp1cmVkX0RhdGEgJT4lDQogIGZpbHRlcihSZXR1cm4uaW4uUmFuZ2UgPT0gMSwNCiAgICAgICAgICFpcy5uYShJbmp1cnkuVmFyaWFiaWxpdHkpKQ0KDQojdGFraW5nIG9ubHkgcGxheWVycyB3aG8gZGlkIG5vdCByZWNvdmVyIGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZQ0KT3V0X1JhbmdlIDwtIEluanVyZWRfRGF0YSAlPiUNCiAgZmlsdGVyKFJldHVybi5pbi5SYW5nZSA9PSAwLA0KICAgICAgICAgIWlzLm5hKEluanVyeS5WYXJpYWJpbGl0eSkpDQpgYGANCg0KDQpgYGB7ciBCb29zdHJhcHBpbmcgdmFyaWFuY2VzIGFjcm9zcyBib3RoIGdyb3Vwc30NCiNtYWtpbmcgZGF0YSBmcmFtZSB0byBob2xkIHZhbHVlcw0KUmFuZ2VfVmFyaWFuY2VzIDwtIGRhdGEuZnJhbWUoaW4uYXZnLnZhciA9IHJlcChOQSwgNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXQuYXZnLnZhciA9IHJlcChOQSwgNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWZmLmF2Zy52YXIgPSByZXAoTkEsIDUwMDApKQ0KDQojYm9vdHN0cmFwcGluZyA1MDAwIHRpbWVzDQpmb3IoaSBpbiAxOjUwMDApew0KICBzZXQuc2VlZChpKQ0KICANCiAgI3NhbXBsZSBmcm9tIHBsYXllcnMgd2hvIHJlY292ZXJlZCBpbiBwcmVkaWN0ZWQgcmFuZ2UNCiAgaW5fc2FtcGxlIDwtIHNhbXBsZV9uKEluX1JhbmdlLCBzaXplPTMwOCwgcmVwbGFjZT1UUlVFKQ0KICAjc2FtcGxlIGZybyBwbGF5ZXJzIHdobyBkaWQgbm90IHJlY292ZXIgaW4gcHJlZGljdGVkIHJhbmdlDQogIG91dF9zYW1wbGUgPC0gc2FtcGxlX24oT3V0X1JhbmdlLCBzaXplPTQ3NywgcmVwbGFjZT1UUlVFKQ0KICANCiAgDQogICNjYWxjdWxhdGluZyB2YXJpYW5jZXMgb2YgZWFjaCBzYW1wbGUgYW5kIHN0b3JpbmcgaW4gZGF0YSBmcmFtZQ0KICBSYW5nZV9WYXJpYW5jZXNbaSwxXSA8LSBtZWFuKGluX3NhbXBsZSRJbmp1cnkuVmFyaWFiaWxpdHkpDQogIFJhbmdlX1ZhcmlhbmNlc1tpLDJdIDwtIG1lYW4ob3V0X3NhbXBsZSRJbmp1cnkuVmFyaWFiaWxpdHkpDQogIFJhbmdlX1ZhcmlhbmNlc1tpLDNdIDwtIFJhbmdlX1ZhcmlhbmNlc1tpLDJdIC0gUmFuZ2VfVmFyaWFuY2VzW2ksMV0gI2RpZmYgaW4gdmFyaWFuY2VzDQp9DQpgYGANCg0KDQpgYGB7ciBQbG90dGluZyByZXN1bHRzIGZyb20gYm9vdHN0cmFwfQ0KI3Bsb3R0aW5nIGRpZmZlcmVuY2VzIGluIGF2ZXJhZ2UgdmFyaWFuY2UNCmdncGxvdChkYXRhPVJhbmdlX1ZhcmlhbmNlcywgYWVzKGRpZmYuYXZnLnZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogICNhZGRpbmcgaW4gOTAlIENJIChkb2VzIG5vdCBpbmNsdWRlIDApDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKFJhbmdlX1ZhcmlhbmNlcyRkaWZmLmF2Zy52YXIsIDAuMDUpLCBjb2xvciA9IiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKFJhbmdlX1ZhcmlhbmNlcyRkaWZmLmF2Zy52YXIsIDAuOTUpLCBjb2xvciA9IiNDRkI4N0MiKSArDQogIGxhYnModGl0bGUgPSAiRXN0aW1hdGVkIERpZmZlcmVuY2UgaW4gQXZlcmFnZSBWYXJpYW5jZSBvZiBSdW5uaW5nIEltYmFsYW5jZSIsDQogICAgICAgeD0gIkRpZmZlcmVuY2UgaW4gQXZlcmFnZSBWYXJpYW5jZSBSdW5uaW5nIEltYmFsYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNwbG90dGluZyBhdmVyYWdlIHZhcmlhbmNlcyBvZiBkaWZmZXJlbmNlIGdyb3Vwcw0KZ2dwbG90KGRhdGE9UmFuZ2VfVmFyaWFuY2VzKSArDQogICNwbGF5ZXJzIHdobyByZWNvdmVyZWQgaW4gcHJlZGljdGVkIHRpbWUgZnJhbWUNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGluLmF2Zy52YXIpLCBhbHBoYSA9IDAuNzUpICsNCiAgI3BsYXllcnMgd2hvIGRpZCBub3QgcmVjb3ZlciBpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKG91dC5hdmcudmFyKSwgYWxwaGEgPSAwLjc1LCBmaWxsID0iI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgQXZlcmFnZSBWYXJpYW5jZSBpbiBSdW5uaW5nIEltYmFsYW5jZSIsIA0KICAgICAgIHg9IkF2ZXJhZ2UgVmFyaWFuY2UgaW4gUnVubmluZyBJbWJhbGFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3IgUmVtb3ZpbmcgdW5uZWNlc3Nhcnkgb2JqZWN0cyBjcmVhdGVkIGR1cmluZyB0aGlzIGFuYWx5c2lzfQ0KcmVtb3ZlKHAsaSxqLHJvd3MsdGVuX2N2LCBjb3N0LCBJbmp1cmVkX0RhdGEsIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nLCBJbmp1cmVkX3Rlc3QsIEluanVyZWRfdHJhaW4sIFJhbmdlX1ZhcmlhbmNlcywgcmV0dXJuX3RvX3BsYXlfY3YsIHJldHVybl90b19wbGF5X21vZGVsLCBleHBlY3RlZF9yZXR1cm4sIGluanVyeV9kYXRlcywgSW5fUmFuZ2UsIE91dF9SYW5nZSwgaW5fc2FtcGxlLCBvdXRfc2FtcGxlKQ0KYGBg